jwt

package
v0.15.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2024 License: Apache-2.0 Imports: 32 Imported by: 0

README

JWT

The JWT package provides support for working with JSON Web Tokens. This includes encoding and decoding tokens, as well as signing and verifying the signature of tokens.

JWK Store

In order to sign token (during encoding) and verify token (during decoding), the JWT package requires a JWK store. The JWK store is responsible for providing the private and public keys used in the signing and verification process. These implementations of the JWK stores are provided:

  1. FileJwkStore
  2. RemoteJwkStore

For testing, we also provide

  1. SingleJwkStore - A JWK store that contains a randomly generated private key for the specified algorithm.
  2. StaticJwkStore - A JWK store that contains a static list of randomly generated private key for the specified algorithm.
FileJwkStore

This store is used to load JWKs from pem files. The file location is specified using configuration properties. This is the default store used in the authorization server configuration.

security:
  keys:
    my-key-name:
      id: my-key-id
      format: pem
      file: my-key-file.pem

The FileJwkStore will load all the keys under the security.keys property. The pem file under each key name can contain one or more keys. Key name is a way to categorize the key by usage. For example, you may want to have a different set of keys for signing and encryption.

If the pem file contains one key. The key id of the key will equal to the name of the key. In this example, if my-key-file.pem contains only one key. That key's key id will be my-key-name.

If the pem file contains multiple key. The key id of the key will either be based on the id property if it's provided. Or it will be generated based on elements of the public key. In this example, since id is provided, the key id will be my-key-id-1 and my-key-id-2 etc.

Key Rotation

The FileJwkStore supports key rotation. If the pem file contains multiple keys, the LoadByName will return the current key for that name. After Rotate is called, the current key will be moved to the next key in the pem file.

Supported Key Types

The FileJwkStore supports the following key types:

  • RSA: PKCS8 unencrypted format. Tradition encrypted and unencrypted format.
  • ECDSA: PKCS8 unencrypted format. Tradition encrypted and unencrypted format.
  • ED25519: PKCS8 unencrypted format.
  • HMAC: Custom unencrypted format.

See the testdata directory for examples of pem files and how to generate them using openssl.

Use HMAC Key with Caution

HMAC key is a symmetric key. This file store supports HMAC key. However, it should be used with caution. By default, the HMAC key is included in the jwks endpoint which is by default public. If you want to use HMAC key, you should secure the jwks endpoint in your application, or encrypt the jwks content by providing your own jwks implementation instead of the default one.

RemoteJwkStore

This store is used to load JWKs from a remote endpoint. It's usually used when your application needs to verify the jwt signature issued from an authorization server that publishes its public keys through its jwks endpoint.

Documentation

Index

Constants

View Source
const (
	JwtHeaderType      = "typ"
	JwtHeaderAlgorithm = "alg"
	JwtHeaderKid       = "kid"
)
View Source
const (
	JwkTypeEC    = `EC`
	JwkTypeRSA   = `RSA`
	JwkTypeOctet = `oct`
	JwkTypeEdDSA = `OKP`
)
View Source
const CryptoKeysPropertiesPrefix = "security"

Variables

View Source
var (
	SymmetricSigningMethods = []jwt.SigningMethod{
		jwt.SigningMethodHS256, jwt.SigningMethodHS384, jwt.SigningMethodHS512,
	}

	AsymmetricSigningMethods = []jwt.SigningMethod{
		jwt.SigningMethodRS256, jwt.SigningMethodRS384, jwt.SigningMethodRS512,
		jwt.SigningMethodES256, jwt.SigningMethodES384, jwt.SigningMethodES512,
		jwt.SigningMethodPS256, jwt.SigningMethodPS384, jwt.SigningMethodPS512,
		jwt.SigningMethodEdDSA,
	}
	SupportedSigningMethods = append(AsymmetricSigningMethods, SymmetricSigningMethods...)
)

Functions

func ParseJwtHeaders

func ParseJwtHeaders(jwtValue string) (map[string]interface{}, error)

ParseJwtHeaders extract JWT's headers without verifying the token

Types

type CryptoKeyProperties

type CryptoKeyProperties struct {
	Id        string `json:"id"`
	KeyFormat string `json:"format"`
	Location  string `json:"file"`
	Password  string `json:"password"`
}

func (CryptoKeyProperties) Format

type CryptoProperties

type CryptoProperties struct {
	Keys map[string]CryptoKeyProperties `json:"keys"`
	Jwt  JwtProperties                  `json:"jwt"`
}

func BindCryptoProperties

func BindCryptoProperties(ctx *bootstrap.ApplicationContext) CryptoProperties

BindCryptoProperties create and bind CryptoProperties, with a optional prefix

func NewCryptoProperties

func NewCryptoProperties() *CryptoProperties

NewCryptoProperties create a CryptoProperties with default values

type FileJwkStore

type FileJwkStore struct {
	// contains filtered or unexported fields
}

FileJwkStore implements JwkStore and JwkRotator This store uses load key files for public and private keys. File locations and "kids" are read from properties. And rotate between pre-defined keys The properties are structured as follows:

keys:
 my-key-name:
   id: my-key-id
   format: pem
   file: my-key-file.pem

Keys loaded under the same key name will all have the same name. The LoadByName method will load one of the keys. Which key will be loaded is determined by the current index for that name. The Rotate method will increment the index for that name. If the pem file contains one key, the key id will be the same as the key name.

If the pem file contains multiple keys, the following rules will be used to generate the key id:

If id property is provided, the actual key id will be the property id plus an integer suffix.
If id property is not provided, the actual key id will be generated based on elements of the public key. The ID value
will be consistent across restarts.

Supports PEM format. Supports: 1. PKCS8 unencrypted private key (rsa, ecdsa, ed25519) 2. traditional unencrypted private key and encrypted private key (rsa and ecdsa) 3. traditional public key (pkcs1 for rsa or pkix for rsa, PKIX for ecdsa and ed25519) 4. x509 certificate (rsa, ecdsa, ed25519) 5. HMAC key (using custom label "HMAC KEY", i.e. -----BEGIN HMAC KEY-----)

Note that if HMAC is used, the application must be responsible for securing the jwks endpoint, or encrypt the jwks content. This is because HMAC keys are symmetric and should not be exposed to public. By default, the jwks endpoint is not secured.

func NewFileJwkStore

func NewFileJwkStore(props CryptoProperties) *FileJwkStore

func (*FileJwkStore) LoadAll

func (s *FileJwkStore) LoadAll(_ context.Context, names ...string) ([]Jwk, error)

func (*FileJwkStore) LoadByKid

func (s *FileJwkStore) LoadByKid(_ context.Context, kid string) (Jwk, error)

func (*FileJwkStore) LoadByName

func (s *FileJwkStore) LoadByName(_ context.Context, name string) (Jwk, error)

func (*FileJwkStore) Rotate

func (s *FileJwkStore) Rotate(_ context.Context, name string) error

type GenericJwk added in v0.15.0

type GenericJwk struct {
	// contains filtered or unexported fields
}

GenericJwk implements Jwk

func (*GenericJwk) Id added in v0.15.0

func (k *GenericJwk) Id() string

func (*GenericJwk) MarshalJSON added in v0.15.0

func (k *GenericJwk) MarshalJSON() ([]byte, error)

func (*GenericJwk) Name added in v0.15.0

func (k *GenericJwk) Name() string

func (*GenericJwk) Public added in v0.15.0

func (k *GenericJwk) Public() crypto.PublicKey

func (*GenericJwk) UnmarshalJSON added in v0.15.0

func (k *GenericJwk) UnmarshalJSON(data []byte) error

type GenericPrivateJwk added in v0.15.0

type GenericPrivateJwk struct {
	GenericJwk
	// contains filtered or unexported fields
}

GenericPrivateJwk implements Jwk and PrivateJwk

func (*GenericPrivateJwk) Private added in v0.15.0

func (k *GenericPrivateJwk) Private() crypto.PrivateKey

type Jwk

type Jwk interface {
	Id() string
	Name() string
	Public() crypto.PublicKey
}

func NewJwk added in v0.15.0

func NewJwk(kid string, name string, pubKey crypto.PublicKey) Jwk

NewJwk new Jwk with specified public key Supported public key types:

  • *rsa.PublicKey
  • *ecdsa.PublicKey
  • ed25519.PublicKey
  • []byte (MAC secret)
  • any key implementing: interface{ Equal(x crypto.PublicKey) bool }

func ParseJwk added in v0.15.0

func ParseJwk(jsonData []byte) (Jwk, error)

ParseJwk parse Jwk from JSON as specified in RFC 7517 and RFC 7518. Note: Private key information is ignored in the parsed Jwk. Supported public key types: - *rsa.PublicKey (kty = RSA) - *ecdsa.PublicKey (kty = EC) - ed25519.PublicKey (kty = OKP) - []byte (symmetric key, e.g. MAC secret) ((kty = oct)

See: RFC7517 https://datatracker.ietf.org/doc/html/rfc7517 See: RFC7518 https://datatracker.ietf.org/doc/html/rfc7518

type JwkRotator

type JwkRotator interface {
	JwkStore
	// Rotate change JWK of given name to next candicate
	Rotate(ctx context.Context, name string) error
}

type JwkStore

type JwkStore interface {
	// LoadByKid returns the JWK associated with given KID.
	// This method is usually used when decoding/verifiying JWT token
	LoadByKid(ctx context.Context, kid string) (Jwk, error)

	// LoadByName returns the JWK associated with given name.
	// The method might return different JWK for same name, if the store is also support rotation
	// This method is usually used when encoding/encrypt JWT token
	// Note: if the store does not support rotation (i.e. it doest not implement JwkRotator),
	// this store could use the name as the jwk id. Doing so would allow the encoder to not
	// add a "kid" header to the JWT token. This allows the use case where the JWT key is agreed upon by
	// both the encoder and decoder through an out-of-band mechanism without using "kid".
	// See the comment in SignedJwtEncoder.Encode for more details
	LoadByName(ctx context.Context, name string) (Jwk, error)

	// LoadAll return all JWK with given names. If name is not provided, all JWK is returned
	LoadAll(ctx context.Context, names ...string) ([]Jwk, error)
}

type JwtDecoder

type JwtDecoder interface {
	Decode(ctx context.Context, token string) (oauth2.Claims, error)
	DecodeWithClaims(ctx context.Context, token string, claims interface{}) error
}

type JwtEncoder

type JwtEncoder interface {
	Encode(ctx context.Context, claims interface{}) (string, error)
}

type JwtProperties

type JwtProperties struct {
	KeyName string `json:"key-name"`
}

type KeyFormatType

type KeyFormatType string
const (
	KeyFileFormatPem KeyFormatType = "pem"
)

type PlaintextJwtDecoder

type PlaintextJwtDecoder struct {
	// contains filtered or unexported fields
}

PlaintextJwtDecoder implements JwtEncoder

func NewPlaintextJwtDecoder

func NewPlaintextJwtDecoder() *PlaintextJwtDecoder

func (*PlaintextJwtDecoder) Decode

func (dec *PlaintextJwtDecoder) Decode(ctx context.Context, tokenString string) (oauth2.Claims, error)

func (*PlaintextJwtDecoder) DecodeWithClaims

func (dec *PlaintextJwtDecoder) DecodeWithClaims(_ context.Context, tokenString string, claims interface{}) (err error)

type PrivateJwk

type PrivateJwk interface {
	Jwk
	Private() crypto.PrivateKey
}

func NewPrivateJwk added in v0.15.0

func NewPrivateJwk(kid string, name string, privKey crypto.PrivateKey) PrivateJwk

NewPrivateJwk new PrivateJwk with specified private key Supported private key types:

  • *rsa.PrivateKey
  • *ecdsa.PrivateKey
  • ed25519.PrivateKey
  • []byte (MAC secret)
  • any key implementing: interface{ Public() crypto.PublicKey Equal(x crypto.PrivateKey) bool }

type RemoteJwkConfig added in v0.15.0

type RemoteJwkConfig struct {
	// HttpClient the underlying http.Client to use. Default: http.DefaultClient
	HttpClient *http.Client
	// JwkSetURL the URL of JWKSet endpoint for getting all JWKs. Default: "http://localhost:8900/auth/v2/jwks"
	// e.g. http://localhost:8900/auth/v2/jwks
	JwkSetURL string
	// JwkBaseURL the base URL of the endpoint for getting JWK by kid (without tailing slash). The actual URL would be "JwkBaseURL/<kid>".
	// (Optional) When not set (empty string), the JwkSetURL is used. Default: "http://localhost:8900/auth/v2/jwks"
	// e.g. JwkBaseURL = "http://localhost:8900/auth/v2/jwks", actual URL is "http://localhost:8900/auth/v2/jwks/<kid>"
	JwkBaseURL string
	// JwkSetRequestFunc a function that create http.Request for JWKSet endpoint. When set, override JwkSetURL.
	// (Optional) When not set, JwkSetURL is used with GET method.
	JwkSetRequestFunc func(ctx context.Context) *http.Request
	// JwkRequestFunc a function that create http.Request for "get JWK by kid". When set, override JwkBaseURL.
	// (Optional) When not set, JwkBaseURL is used with GET method. If JwkBaseURL is not set either, JWKSet endpoint is used.
	JwkRequestFunc func(ctx context.Context, kid string) *http.Request
	// DisableCache disable internal caching. If the cache is disabled, the store would invoke an external HTTP transaction
	// everytime when any of store's method is called. Default: false
	DisableCache bool
	// TTL cache setting. TTL controls how long the HTTP result is kept in cache.
	TTL time.Duration
	// RetryBackoff cache setting. It controls how long to wait between failed HTTP retries.
	RetryBackoff time.Duration
	// Retry cache setting. It controls how many times the cache would retry for failed HTTP transaction.
	Retry int
}

type RemoteJwkOptions added in v0.15.0

type RemoteJwkOptions func(cfg *RemoteJwkConfig)

type RemoteJwkStore added in v0.15.0

type RemoteJwkStore struct {
	RemoteJwkConfig
	// contains filtered or unexported fields
}

RemoteJwkStore implements JwkStore and load JWK with public key from an external JWKSet endpoint. Important: Use RemoteJwkStore with JwtDecoder ONLY.

RemoteJwkStore is not capable of decrypt private key from JWK response

Note: LoadByName and LoadAll would treat Jwk's "name" as "kid". Because "name" is introduced for managing

key rotation, which is not applicable to JwtDecoder: JwtDecoder strictly use `kid` if present in header
or default "name" (in such case, should be hard coded globally known "kid")

func NewRemoteJwkStore added in v0.15.0

func NewRemoteJwkStore(opts ...RemoteJwkOptions) *RemoteJwkStore

NewRemoteJwkStore creates a JwkStore that load JWK with public key from an external JWKSet endpoint. Note: Use RemoteJwkStore with JwtDecoder ONLY.

RemoteJwkStore is not capable of decrypt private key from JWK response.

See RemoteJwkStore for more details

func (*RemoteJwkStore) LoadAll added in v0.15.0

func (s *RemoteJwkStore) LoadAll(ctx context.Context, names ...string) ([]Jwk, error)

func (*RemoteJwkStore) LoadByKid added in v0.15.0

func (s *RemoteJwkStore) LoadByKid(ctx context.Context, kid string) (Jwk, error)

func (*RemoteJwkStore) LoadByName added in v0.15.0

func (s *RemoteJwkStore) LoadByName(ctx context.Context, name string) (Jwk, error)

type SignedJwtDecoder added in v0.15.0

type SignedJwtDecoder struct {
	// contains filtered or unexported fields
}

SignedJwtDecoder implements JwtEncoder

func NewSignedJwtDecoder added in v0.15.0

func NewSignedJwtDecoder(opts ...VerifyOptions) *SignedJwtDecoder

func (*SignedJwtDecoder) Decode added in v0.15.0

func (dec *SignedJwtDecoder) Decode(ctx context.Context, tokenString string) (oauth2.Claims, error)

func (*SignedJwtDecoder) DecodeWithClaims added in v0.15.0

func (dec *SignedJwtDecoder) DecodeWithClaims(ctx context.Context, tokenString string, claims interface{}) (err error)

type SignedJwtEncoder added in v0.15.0

type SignedJwtEncoder struct {
	// contains filtered or unexported fields
}

SignedJwtEncoder implements JwtEncoder. It encodes claims with crypto signature of choice. Encoder may return error if private key is not compatible with signing method

func NewSignedJwtEncoder added in v0.15.0

func NewSignedJwtEncoder(opts ...SigningOptions) *SignedJwtEncoder

NewSignedJwtEncoder create a JwtEncoder that sign JWT with provided method. Depending on the sign method, provided JwkStore should supply proper private keys. Note: When using HS algorithms, the HMAC secret is treated as both public and private key,

and it would be exposed via JWKS endpoint. It is service implementer's responsibility to
protect the JWKS endpoint to prevent accidental leaking of HMAC secret.

func (*SignedJwtEncoder) Encode added in v0.15.0

func (enc *SignedJwtEncoder) Encode(ctx context.Context, claims interface{}) (string, error)

type SigningOption added in v0.15.0

type SigningOption struct {
	JwkStore JwkStore
	JwkName  string
	Method   jwt.SigningMethod
}

type SigningOptions added in v0.15.0

type SigningOptions func(opt *SigningOption)

func SignWithJwkStore added in v0.15.0

func SignWithJwkStore(store JwkStore, jwkName string) SigningOptions

SignWithJwkStore is a SigningOptions that set JwkStore and key name to use when signing

func SignWithMethod added in v0.15.0

func SignWithMethod(method jwt.SigningMethod) SigningOptions

SignWithMethod is SigningOptions that specify the method to use. When set to nil, the encoder would attempt to use the private key type to resolve signing method.

type SingleJwkStore

type SingleJwkStore struct {
	Kid           string
	SigningMethod jwt.SigningMethod
	// contains filtered or unexported fields
}

SingleJwkStore implements JwkStore This store always returns single JWK if Kid matches, return error if not This store is majorly for testing

func NewSingleJwkStore

func NewSingleJwkStore(kid string) *SingleJwkStore

NewSingleJwkStore Deprecated: Use NewSingleJwkStoreWithOptions

func NewSingleJwkStoreWithOptions added in v0.15.0

func NewSingleJwkStoreWithOptions(opts ...func(s *SingleJwkStore)) *SingleJwkStore

func (*SingleJwkStore) LazyInit added in v0.15.0

func (s *SingleJwkStore) LazyInit() (err error)

func (*SingleJwkStore) LoadAll

func (s *SingleJwkStore) LoadAll(_ context.Context, _ ...string) ([]Jwk, error)

func (*SingleJwkStore) LoadByKid

func (s *SingleJwkStore) LoadByKid(_ context.Context, kid string) (Jwk, error)

func (*SingleJwkStore) LoadByName

func (s *SingleJwkStore) LoadByName(_ context.Context, name string) (Jwk, error)

type StaticJwkStore

type StaticJwkStore struct {
	KIDs          []string
	SigningMethod jwt.SigningMethod
	// contains filtered or unexported fields
}

StaticJwkStore implements JwkStore and JwkRotator This store uses "kid" as seed to generate PrivateJwk. For same "kid" the returned key is same. this one is not thread safe

func NewStaticJwkStore

func NewStaticJwkStore(kids ...string) *StaticJwkStore

NewStaticJwkStore Deprecated: Use NewStaticJwkStoreWithOptions

func NewStaticJwkStoreWithOptions added in v0.15.0

func NewStaticJwkStoreWithOptions(opts ...func(s *StaticJwkStore)) *StaticJwkStore

func (*StaticJwkStore) LoadAll

func (s *StaticJwkStore) LoadAll(_ context.Context, _ ...string) ([]Jwk, error)

func (*StaticJwkStore) LoadByKid

func (s *StaticJwkStore) LoadByKid(_ context.Context, kid string) (Jwk, error)

func (*StaticJwkStore) LoadByName

func (s *StaticJwkStore) LoadByName(_ context.Context, _ string) (Jwk, error)

func (*StaticJwkStore) Rotate

func (s *StaticJwkStore) Rotate(_ context.Context, _ string) error

type VerifyOption added in v0.15.0

type VerifyOption struct {
	JwkStore      JwkStore
	JwkName       string
	Methods       []jwt.SigningMethod
	ParserOptions []jwt.ParserOption
}

type VerifyOptions added in v0.15.0

type VerifyOptions func(opt *VerifyOption)

func VerifyWithJwkStore added in v0.15.0

func VerifyWithJwkStore(store JwkStore, jwkName string) VerifyOptions

VerifyWithJwkStore is a VerifyOptions that set JwkStore and default key name to use when verifying. the provided key name is used as fallback if the to-be-verified JWT doesn't have "kid" in header

func VerifyWithMethods added in v0.15.0

func VerifyWithMethods(methods ...jwt.SigningMethod) VerifyOptions

VerifyWithMethods is a VerifyOptions that specify all allowed signing method ("alg" header). By default, it accepts all available signing methods except for plaintext JWT.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL