Documentation ¶
Overview ¶
Package gcpjwt has Google Cloud Platform (Cloud KMS, IAM API, & AppEngine App Identity API) jwt-go implementations. Should work across virtually all environments, on or off of Google's Cloud Platform.
Getting Started ¶
It is highly recommended that you override the default algorithm implementations that you want to leverage a GCP service for in dgrijalva/jwt-go. You otherwise will have to manually pick the verification method for your JWTs and they will place non-standard headers in the rendered JWT (with the exception of signJwt from the IAM API which overwrites the header with its own).
You should only need to override the algorithm(s) you plan to use. It is also incorrect to override overlapping, algorithms such as `gcpjwt.SigningMethodKMSRS256.Override()` and `gcpjwt.SigningMethodIAMJWT.Override()`
Example:
import ( "github.com/someone1/gcp-jwt-go" ) func init() { // Pick one or more of the following to override // Cloud KMS gcpjwt.SigningMethodKMSRS256.Override() // RS256 gcpjwt.SigningMethodKMSPS256.Override() // PS256 gcpjwt.SigningMethodKMSES256.Override() // ES256 gcpjwt.SigningMethodKMSES384.Override() // ES384 // IAM API - This implements RS256 exclusively gcpjwt.SigningMethodIAMJWT.Override() // For signJwt gcpjwt.SigningMethodIAMBlob.Override() // For signBlob // AppEngine - Standard runtime only, does not apply to Flexible runtime, implements RS256 exclusively // You can also use any of the above methods on AppEngine Standard gcpjwt.SigningMethodAppEngine.Override() }
As long as a you override a default algorithm implementation as shown above, using the dgrijalva/jwt-go is mostly unchanged.
Create a Token ¶
Token creation is more/less done the same way as in the dgrijalva/jwt-go package. The key that you need to provide is always going to be a context.Context, usuaully with a configuration object loaded in:
- use gcpjwt.IAMConfig for the SigningMethodIAMJWT and SigningMethodIAMBlob signing methods
- use an appengine.NewContext for the SigningMethodAppEngine signing method
- use gcpjwt.KMSConfig for any of the KMS signing methods
Example:
import ( "context" "net/http" "github.com/dgrijalva/jwt-go" "github.com/someone1/gcp-jwt-go" "google.golang.org/appengine" // only on AppEngine Standard when using the SigningMethodAppEngine signing method ) func makeToken(ctx context.Context) (string, error) string { // Important - if on AppEngine standard, even if you aren't using the SigningMethodAppEngine signing method // you must pass around the appengine.NewContext context as it is required for the API calls all methods must // make. var key interface{} claims := &jwt.StandardClaims{ ExpiresAt: 15000, Issuer: "test", } token := jwt.NewWithClaims(gcpjwt.SigningMethodGCPJWT, claims) // Prepare your signing key // For SigningMethodIAMJWT or SigningMethodIAMBlob config := &gcpjwt.IAMConfig{ ServiceAccount: "app-id@appspot.gserviceaccount.com", IAMType: gcpjwt.IAMJwtType, // or gcpjwt.IAMBlobType } key = gcpjwt.NewIAMContext(ctx, config) // For any KMS signing method config := &gcpjwt.KMSConfig{ KeyPath: "name=projects/<project-id>/locations/<location>/keyRings/<key-ring-name>/cryptoKeys/<key-name>/cryptoKeyVersions/<key-version>", } key = gcpjwt.NewKMSContext(ctx, config) // For SigningMethodAppEngine key = ctx // For all signing methods EXCEPT signJWT return token.SignedString(key) // For signJwt // !!IMPORTANT!! Due to the way the signJwt API returns tokens, we can't use the standard signing process // to sign signingString, err := token.SigningString() if err != nil { return "", err } return token.Method.Sign(signingString, key) }
Validate a Token ¶
Finally, the steps to validate a token should be straight forward. This library provides you with helper jwt.Keyfunc implementations to do the heavy lifting around getting the public certificates for verification:
- gcpjwt.IAMVerfiyKeyfunc can be used for the IAM API and the AppEngine Standard signing methods
- gcpjwt.AppEngineVerfiyKeyfunc is only available on AppEngine standard and can only be used on JWT signed from the same default service account as the running application
- gcp.KMSVerfiyKeyfunc can be used for the Cloud KMS signing methods
Example:
import ( "context" "time" "strings" "github.com/dgrijalva/jwt-go" "github.com/someone1/gcp-jwt-go" ) func validateToken(ctx context.Context, tokenString string) (*jwt.Token, error) { // Important - if on AppEngine standard, even if you aren't using the SigningMethodAppEngine signing method // you must pass around the appengine.NewContext context as it is required for the API calls all methods must // make. var keyFunc jwt.Keyfunc // Prepare your key function // For SigningMethodIAMJWT or SigningMethodIAMBlob or SigningMethodAppEngine config := &gcpjwt.IAMConfig{ ServiceAccount: "app-id@appspot.gserviceaccount.com", IAMType: gcpjwt.IAMJwtType, // or gcpjwt.IAMBlobType (use the Blob type if you used the SigningMethodAppEngine before) EnableCache: true, // Enable the certificate cache so we don't fetch it on every verification - RECOMMENDED } keyFunc = gcpjwt.IAMVerfiyKeyfunc(ctx, config) // For any KMS signing method config := &gcpjwt.KMSConfig{ KeyPath: "name=projects/<project-id>/locations/<location>/keyRings/<key-ring-name>/cryptoKeys/<key-name>/cryptoKeyVersions/<key-version>", } keyFunc = gcpjwt.KMSVerfiyKeyfunc(ctx, config) // For SigningMethodAppEngine only on AppEngine Standard keyFunc = gcpjwt.AppEngineVerfiyKeyfunc(ctx, true, time.Hour) // If you called an Override function as recommended above, for both signing and verifying a token, you can use // the regular verification steps - and the same goes if you did NOT call it for both signing and verifying (using non-standard 'alg' headers) // EXCEPT for the signJwt IAM API signing method which overwrites the header's alg to RS256 return jwt.Parse(tokenString, keyFunc) // The following is an extreme and advanced use-case - it is NOT recommended but here for those who need it. // // If we need to manually override the detected jwt.SigningMethod based on the 'alg' header // This is basically copying the https://github.com/dgrijalva/jwt-go/blob/master/parser.go#L23 ParseWithClaims function here but forcing our own method vs getting one based on the Alg field // Or Try and parse, Ignore the result and try with the proper method: token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return nil, nil }) parts := strings.Split(token.Raw, ".") token.Method = gcpjwt.SigningMethodIAMJWT // or whichever method you want to force if err := token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, keyFunc); err != nil { return nil, err } else { token.Valid = true } return token, nil }
Index ¶
- Constants
- Variables
- func AppEngineVerfiyKeyfunc(ctx context.Context, enableCache bool, cacheExpiration time.Duration) jwt.Keyfunc
- func IAMVerfiyKeyfunc(ctx context.Context, config *IAMConfig) jwt.Keyfunc
- func KMSVerfiyKeyfunc(ctx context.Context, config *KMSConfig) (jwt.Keyfunc, error)
- func NewIAMContext(parent context.Context, val *IAMConfig) context.Context
- func NewKMSContext(parent context.Context, val *KMSConfig) context.Context
- type IAMConfig
- type KMSConfig
- type SigningMethodAppEngineImpl
- type SigningMethodIAM
- type SigningMethodKMS
- func (s *SigningMethodKMS) Alg() string
- func (s *SigningMethodKMS) Hash() crypto.Hash
- func (s *SigningMethodKMS) Override()
- func (s *SigningMethodKMS) Sign(signingString string, key interface{}) (string, error)
- func (s *SigningMethodKMS) Verify(signingString, signature string, key interface{}) error
Constants ¶
const ( // IAMBlobType is used as a hint in the config to let various parts of the library know you intend to use the // signBlob IAM API. IAMBlobType iamType = iota + 1 // IAMJwtType is used as a hint in the config to let various parts of the library know you intend to use the // signJwt IAM API. IAMJwtType )
Variables ¶
var ( // ErrMissingConfig is returned when Sign or Verify did not find a configuration in the provided context ErrMissingConfig = errors.New("gcpjwt: missing configuration in provided context") )
Functions ¶
func AppEngineVerfiyKeyfunc ¶
func AppEngineVerfiyKeyfunc(ctx context.Context, enableCache bool, cacheExpiration time.Duration) jwt.Keyfunc
AppEngineVerfiyKeyfunc is a helper meant that returns a jwt.Keyfunc. It will handle pulling and selecting the certificates to verify signatures with, caching when enabled.
func IAMVerfiyKeyfunc ¶
IAMVerfiyKeyfunc is a helper meant that returns a jwt.Keyfunc. It will handle pulling and selecting the certificates to verify signatures with, caching when enabled.
func KMSVerfiyKeyfunc ¶
KMSVerfiyKeyfunc is a helper meant that returns a jwt.Keyfunc. It will handle pulling and selecting the certificates to verify signatures with, caching the public key in memory. It is not valid to modify the KMSConfig provided after calling this function, you must call this again if changes to the config's KeyPath are made. Note that the public key is retrieved when creating the key func and returned for each call to the returned jwt.Keyfunc. https://cloud.google.com/kms/docs/retrieve-public-key#kms-howto-retrieve-public-key-go
func NewIAMContext ¶
NewIAMContext returns a new context.Context that carries a provided IAMConfig value
Types ¶
type IAMConfig ¶
type IAMConfig struct { // ProjectID is the project id that contains the service account you want to sign with. Defaults to "-" to infer the project from the account // Depcrecated: This field is no longer used as the API will reject all values other than "-". ProjectID string // Service account can be the email address or the uniqueId of the service account used to sign the JWT with ServiceAccount string // EnableCache will enable the in-memory caching of public certificates. // The cache will expire certificates when an expiration is known or fallback to the configured CacheExpiration EnableCache bool // CacheExpiration is the default time to keep the certificates in cache if no expiration time is provided // Use a value of 0 to disable the expiration time fallback. Max reccomneded value is 24 hours. // https://cloud.google.com/iam/docs/understanding-service-accounts#managing_service_account_keys CacheExpiration time.Duration // IAMType is a helper used to help clarify which IAM signing method this config is meant for. // Used for the jwtmiddleware and oauth2 packages. IAMType iamType // IAMService is a user provided service client that should be used when communicating with the iamcredentials API, // otherwuse the default service will be used. IAMService *iamcredentials.Service // OAuth2HTTPClient is a user provided oauth2 authenticated *http.Client to use, google.DefaultClient used otherwise // Used for signing requests // Depcrecated: This field is no longer used. Use IAMClient instead OAuth2HTTPClient *http.Client // Client is a user provided *http.Client to use, http.DefaultClient is used otherwise (AppEngine URL Fetch Supported) // Used for verify requests Client *http.Client sync.RWMutex // contains filtered or unexported fields }
IAMConfig is relevant for both the signBlob and signJWT IAM API use-cases
func IAMFromContext ¶
IAMFromContext extracts a IAMConfig from a context.Context
type KMSConfig ¶
type KMSConfig struct { // KeyPath is the name of the key to use in the format of: // "name=projects/*/locations/*/keyRings/*/cryptoKeys/*/cryptoKeyVersions/*" KeyPath string // KMSClient to use for calls to the API. If nil, a standard one will be initiated KMSClient *kms.KeyManagementClient }
KMSConfig is used to sign/verify JWTs with Google Cloud KMS
func KMSFromContext ¶
KMSFromContext extracts a KMSConfig from a context.Context
type SigningMethodAppEngineImpl ¶
type SigningMethodAppEngineImpl struct { *SigningMethodIAM sync.RWMutex // contains filtered or unexported fields }
SigningMethodAppEngineImpl implements singing JWT's using the built-in AppEngine signing method. This method uses a private key unique to your AppEngine application and the key may rotate from time to time. https://cloud.google.com/appengine/docs/go/reference#SignBytes https://cloud.google.com/appengine/docs/go/appidentity/#Go_Asserting_identity_to_other_systems
var (
SigningMethodAppEngine *SigningMethodAppEngineImpl
)
func (*SigningMethodAppEngineImpl) KeyID ¶
func (s *SigningMethodAppEngineImpl) KeyID() string
KeyID will return the last used KeyID to sign the JWT. Helper function for adding the kid header to your token.
type SigningMethodIAM ¶
type SigningMethodIAM struct {
// contains filtered or unexported fields
}
SigningMethodIAM is the base implementation for the signBlob and signJwt IAM API JWT signing methods. Not to be used on its own!
var ( // SigningMethodIAMBlob implements signing JWTs with the IAM signBlob API. // https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob SigningMethodIAMBlob *SigningMethodIAM )
var ( // SigningMethodIAMJWT implements signing JWTs with the IAM signJwt API. // https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signJwt SigningMethodIAMJWT *SigningMethodIAM )
func (*SigningMethodIAM) Alg ¶
func (s *SigningMethodIAM) Alg() string
Alg will return the JWT header algorithm identifier this method is configured for.
func (*SigningMethodIAM) Override ¶
func (s *SigningMethodIAM) Override()
Override will override the default JWT implementation of the signing function this IAM API type implements.
func (*SigningMethodIAM) Sign ¶
func (s *SigningMethodIAM) Sign(signingString string, key interface{}) (string, error)
Sign implements the Sign method from jwt.SigningMethod. For this signing method, a valid context.Context must be passed as the key containing a IAMConfig value. NOTE: The HEADER IS IGNORED for the signJWT API as the API will add its own
func (*SigningMethodIAM) Verify ¶
func (s *SigningMethodIAM) Verify(signingString, signature string, key interface{}) error
Verify implements the Verify method from jwt.SigningMethod. This will expect key type of []*rsa.PublicKey. https://firebase.google.com/docs/auth/admin/verify-id-tokens
type SigningMethodKMS ¶
type SigningMethodKMS struct {
// contains filtered or unexported fields
}
SigningMethodKMS implements the jwt.SiginingMethod interface for Google's Cloud KMS service
var ( // SigningMethodKMSRS256 leverages Cloud KMS for RS256 algorithms, use with: // RSA_SIGN_PKCS1_2048_SHA256 // RSA_SIGN_PKCS1_3072_SHA256 // RSA_SIGN_PKCS1_4096_SHA256 SigningMethodKMSRS256 *SigningMethodKMS // SigningMethodKMSPS256 leverages Cloud KMS for PS256 algorithms, use with: // RSA_SIGN_PSS_2048_SHA256 // RSA_SIGN_PSS_3072_SHA256 // RSA_SIGN_PSS_4096_SHA256 SigningMethodKMSPS256 *SigningMethodKMS // SigningMethodKMSES256 leverages Cloud KMS for the ES256 algorithm, use with: // EC_SIGN_P256_SHA256 SigningMethodKMSES256 *SigningMethodKMS // SigningMethodKMSES384 leverages Cloud KMS for the ES256 algorithm, use with: // EC_SIGN_P384_SHA384 SigningMethodKMSES384 *SigningMethodKMS )
Support for the Google Cloud KMS Asymmetric Signing Algorithms: https://cloud.google.com/kms/docs/algorithms
func (*SigningMethodKMS) Alg ¶
func (s *SigningMethodKMS) Alg() string
Alg will return the JWT header algorithm identifier this method is configured for.
func (*SigningMethodKMS) Hash ¶
func (s *SigningMethodKMS) Hash() crypto.Hash
Hash will return the crypto.Hash used for this signing method
func (*SigningMethodKMS) Override ¶
func (s *SigningMethodKMS) Override()
Override will override the default JWT implementation of the signing function this Cloud KMS type implements.
func (*SigningMethodKMS) Sign ¶
func (s *SigningMethodKMS) Sign(signingString string, key interface{}) (string, error)
Sign implements the Sign method from jwt.SigningMethod. For this signing method, a valid context.Context must be passed as the key containing a KMSConfig value. https://cloud.google.com/kms/docs/create-validate-signatures#kms-howto-sign-go
func (*SigningMethodKMS) Verify ¶
func (s *SigningMethodKMS) Verify(signingString, signature string, key interface{}) error
Verify does a pass-thru to the appropriate jwt.SigningMethod for this signing algorithm and expects the same key https://cloud.google.com/kms/docs/create-validate-signatures#validate_ec_signature https://cloud.google.com/kms/docs/create-validate-signatures#validate_rsa_signature