Documentation ¶
Overview ¶
Package jwt provides signature verification and claims set validation for JSON Web Tokens (JWT) of the JSON Web Signature (JWS) form.
JWT claims set validation provided by the package includes the option to validate all registered claim names defined in https://tools.ietf.org/html/rfc7519#section-4.1.
JOSE header validation provided by the the package includes the option to validate the "alg" (Algorithm) Header Parameter defined in https://tools.ietf.org/html/rfc7515#section-4.1.
JWT signature verification is supported by providing keys from the following sources:
- JSON Web Key Set (JWKS) URL
- OIDC Discovery mechanism
- Local public keys
JWT signature verification supports the following asymmetric algorithms as defined in https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1:
- RS256: RSASSA-PKCS1-v1_5 using SHA-256
- RS384: RSASSA-PKCS1-v1_5 using SHA-384
- RS512: RSASSA-PKCS1-v1_5 using SHA-512
- ES256: ECDSA using P-256 and SHA-256
- ES384: ECDSA using P-384 and SHA-384
- ES512: ECDSA using P-521 and SHA-512
- PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
- PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
- PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
- EdDSA: Ed25519 using SHA-512
Index ¶
Examples ¶
Constants ¶
const DefaultLeewaySeconds = 150
DefaultLeewaySeconds defines the amount of leeway that's used by default for validating the "nbf" (Not Before) and "exp" (Expiration Time) claims.
Variables ¶
This section is empty.
Functions ¶
func ParsePublicKeyPEM ¶
ParsePublicKeyPEM is used to parse RSA and ECDSA public keys from PEMs. The given data must be of PEM-encoded x509 certificate or PKIX public key forms. It returns an *rsa.PublicKey or *ecdsa.PublicKey.
func SupportedSigningAlgorithm ¶
SupportedSigningAlgorithm returns an error if any of the given Algs are not supported signing algorithms.
Types ¶
type Alg ¶
type Alg string
Alg represents asymmetric signing algorithms
const ( // JOSE asymmetric signing algorithm values as defined by RFC 7518. // // See: https://tools.ietf.org/html/rfc7518#section-3.1 RS256 Alg = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 RS384 Alg = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 RS512 Alg = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 ES256 Alg = "ES256" // ECDSA using P-256 and SHA-256 ES384 Alg = "ES384" // ECDSA using P-384 and SHA-384 ES512 Alg = "ES512" // ECDSA using P-521 and SHA-512 PS256 Alg = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 PS384 Alg = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 PS512 Alg = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 EdDSA Alg = "EdDSA" // Ed25519 using SHA-512 )
type Expected ¶
type Expected struct { // The expected JWT "iss" (issuer) claim value. If empty, validation is skipped. Issuer string // The expected JWT "sub" (subject) claim value. If empty, validation is skipped. Subject string // The expected JWT "jti" (JWT ID) claim value. If empty, validation is skipped. ID string // The list of expected JWT "aud" (audience) claim values to match against. // The JWT claim will be considered valid if it matches any of the expected // audiences. If empty, validation is skipped. Audiences []string // SigningAlgorithms provides the list of expected JWS "alg" (algorithm) header // parameter values to match against. The JWS header parameter will be considered // valid if it matches any of the expected signing algorithms. The following // algorithms are supported: RS256, RS384, RS512, ES256, ES384, ES512, PS256, // PS384, PS512, EdDSA. If empty, defaults to RS256. SigningAlgorithms []Alg // NotBeforeLeeway provides the option to set an amount of leeway to use when // validating the "nbf" (Not Before) claim. If the duration is zero or not // provided, a default leeway of 150 seconds will be used. If the duration is // negative, no leeway will be used. NotBeforeLeeway time.Duration // ExpirationLeeway provides the option to set an amount of leeway to use when // validating the "exp" (Expiration Time) claim. If the duration is zero or not // provided, a default leeway of 150 seconds will be used. If the duration is // negative, no leeway will be used. ExpirationLeeway time.Duration // ClockSkewLeeway provides the option to set an amount of leeway to use when // validating the "nbf" (Not Before), "exp" (Expiration Time), and "iat" (Issued At) // claims. If the duration is zero or not provided, a default leeway of 60 seconds // will be used. If the duration is negative, no leeway will be used. ClockSkewLeeway time.Duration // Now provides the option to specify a func for determining what the current time is. // The func will be used to provide the current time when validating a JWT with respect to // the "nbf" (Not Before), "exp" (Expiration Time), and "iat" (Issued At) claims. If not // provided, defaults to returning time.Now(). Now func() time.Time }
Expected defines the expected claims values to assert when validating a JWT. For claims that involve validation of the JWT with respect to time, leeway fields are provided to account for potential clock skew.
type KeySet ¶
type KeySet interface { // VerifySignature parses the given JWT, verifies its signature, and returns the claims in its payload. // The given JWT must be of the JWS compact serialization form. VerifySignature(ctx context.Context, token string) (claims map[string]interface{}, err error) }
KeySet represents a set of keys that can be used to verify the signatures of JWTs. A KeySet is expected to be backed by a set of local or remote keys.
func NewJSONWebKeySet ¶
NewJSONWebKeySet returns a KeySet that verifies JWT signatures using keys from the JSON Web Key Set (JWKS) at the given jwksURL. The client used to obtain the remote JWKS will verify server certificates using the root certificates provided by jwksCAPEM. If jwksCAPEM is not provided, system certificates are used.
Example ¶
package main import ( "context" "fmt" "log" "github.com/hashicorp/cap/jwt" ) func main() { ctx := context.Background() keySet, err := jwt.NewJSONWebKeySet(ctx, "your_jwks_url", "your_jwks_ca_pem") if err != nil { log.Fatal(err) } token := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleHBfaXNzIiwiZXhwIjoxNTI2MjM5MDIyfQ.XG1xYJcuPMfgu8xkMzVjkYK2WIUyl4-A1Zq1j4Dfr99-PJUN36ZAgi8Fj08modiexXETrg05MqSxkJAE5Czns1IhqEEypx6xfYHSINp0SLKxBFHPA4BCi0IW83T-e225JjjVEGFR_Wo8QM6Rc-qQVJ9bqwKD4kcbQeMACkgGFcgNurtNkOM9vtOEs0Pe9tb4nHYw4ef1stCytTi9GFZwGoHQf0pjpWCpjlxaFIR4vmHQ4YB3w29o_tKN6zqyA2FITnvkzGnaLvdPecJNskRSCPUTRfYcVVNXCOnCvTdpvwK-c4nCs5yGnw3eeFoT6mhQSp39KYti1MpHNQTYwZrLTA" claims, err := keySet.VerifySignature(ctx, token) if err != nil { log.Fatal(err) } fmt.Println(claims) }
Output:
func NewOIDCDiscoveryKeySet ¶
NewOIDCDiscoveryKeySet returns a KeySet that verifies JWT signatures using keys from the JSON Web Key Set (JWKS) published in the discovery document at the given issuer URL. The client used to obtain the remote keys will verify server certificates using the root certificates provided by issuerCAPEM. If issuerCAPEM is not provided, system certificates are used.
Example ¶
package main import ( "context" "fmt" "log" "github.com/hashicorp/cap/jwt" ) func main() { ctx := context.Background() keySet, err := jwt.NewOIDCDiscoveryKeySet(ctx, "your_issuer_url", "your_issuer_ca_pem") if err != nil { log.Fatal(err) } token := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleHBfaXNzIiwiZXhwIjoxNTI2MjM5MDIyfQ.XG1xYJcuPMfgu8xkMzVjkYK2WIUyl4-A1Zq1j4Dfr99-PJUN36ZAgi8Fj08modiexXETrg05MqSxkJAE5Czns1IhqEEypx6xfYHSINp0SLKxBFHPA4BCi0IW83T-e225JjjVEGFR_Wo8QM6Rc-qQVJ9bqwKD4kcbQeMACkgGFcgNurtNkOM9vtOEs0Pe9tb4nHYw4ef1stCytTi9GFZwGoHQf0pjpWCpjlxaFIR4vmHQ4YB3w29o_tKN6zqyA2FITnvkzGnaLvdPecJNskRSCPUTRfYcVVNXCOnCvTdpvwK-c4nCs5yGnw3eeFoT6mhQSp39KYti1MpHNQTYwZrLTA" claims, err := keySet.VerifySignature(ctx, token) if err != nil { log.Fatal(err) } fmt.Println(claims) }
Output:
func NewStaticKeySet ¶
NewStaticKeySet returns a KeySet that verifies JWT signatures using the given publicKeys.
Example ¶
package main import ( "context" "crypto" "crypto/rand" "crypto/rsa" "fmt" "log" "github.com/hashicorp/cap/jwt" ) func main() { ctx := context.Background() rsaKey, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { log.Fatal(err) } keys := []crypto.PublicKey{ rsaKey.Public(), } keySet, err := jwt.NewStaticKeySet(keys) if err != nil { log.Fatal(err) } token := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleHBfaXNzIiwiZXhwIjoxNTI2MjM5MDIyfQ.XG1xYJcuPMfgu8xkMzVjkYK2WIUyl4-A1Zq1j4Dfr99-PJUN36ZAgi8Fj08modiexXETrg05MqSxkJAE5Czns1IhqEEypx6xfYHSINp0SLKxBFHPA4BCi0IW83T-e225JjjVEGFR_Wo8QM6Rc-qQVJ9bqwKD4kcbQeMACkgGFcgNurtNkOM9vtOEs0Pe9tb4nHYw4ef1stCytTi9GFZwGoHQf0pjpWCpjlxaFIR4vmHQ4YB3w29o_tKN6zqyA2FITnvkzGnaLvdPecJNskRSCPUTRfYcVVNXCOnCvTdpvwK-c4nCs5yGnw3eeFoT6mhQSp39KYti1MpHNQTYwZrLTA" claims, err := keySet.VerifySignature(ctx, token) if err != nil { log.Fatal(err) } fmt.Println(claims) }
Output:
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator validates JSON Web Tokens (JWT) by providing signature verification and claims set validation. Validator can contain either a single or multiple KeySets and will attempt to verify the JWT by iterating through the configured KeySets.
func NewValidator ¶
NewValidator returns a Validator that uses the given KeySet to verify JWT signatures.
func (*Validator) Validate ¶
func (v *Validator) Validate(ctx context.Context, token string, expected Expected) (map[string]interface{}, error)
Validate validates JWTs of the JWS compact serialization form.
The given JWT is considered valid if:
- Its signature is successfully verified.
- Its claims set and header parameter values match what's given by Expected.
- It's valid with respect to the current time. This means that the current time must be within the times (inclusive) given by the "nbf" (Not Before) and "exp" (Expiration Time) claims and after the time given by the "iat" (Issued At) claim, with configurable leeway. See Expected.Now() for details on how the current time is provided for validation.
Example ¶
package main import ( "context" "fmt" "log" "github.com/hashicorp/cap/jwt" ) func main() { ctx := context.Background() keySet, err := jwt.NewJSONWebKeySet(ctx, "your_jwks_url", "your_jwks_ca_pem") if err != nil { log.Fatal(err) } validator, err := jwt.NewValidator(keySet) if err != nil { log.Fatal(err) } expected := jwt.Expected{ Issuer: "your_expected_issuer", Subject: "your_expected_subject", ID: "your_expected_jwt_id", Audiences: []string{"your_expected_audiences"}, SigningAlgorithms: []jwt.Alg{jwt.RS256}, } token := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleHBfaXNzIiwiZXhwIjoxNTI2MjM5MDIyfQ.XG1xYJcuPMfgu8xkMzVjkYK2WIUyl4-A1Zq1j4Dfr99-PJUN36ZAgi8Fj08modiexXETrg05MqSxkJAE5Czns1IhqEEypx6xfYHSINp0SLKxBFHPA4BCi0IW83T-e225JjjVEGFR_Wo8QM6Rc-qQVJ9bqwKD4kcbQeMACkgGFcgNurtNkOM9vtOEs0Pe9tb4nHYw4ef1stCytTi9GFZwGoHQf0pjpWCpjlxaFIR4vmHQ4YB3w29o_tKN6zqyA2FITnvkzGnaLvdPecJNskRSCPUTRfYcVVNXCOnCvTdpvwK-c4nCs5yGnw3eeFoT6mhQSp39KYti1MpHNQTYwZrLTA" claims, err := validator.Validate(ctx, token, expected) if err != nil { log.Fatal(err) } fmt.Println(claims) }
Output:
func (*Validator) ValidateAllowMissingIatNbfExp ¶ added in v0.3.0
func (v *Validator) ValidateAllowMissingIatNbfExp(ctx context.Context, token string, expected Expected) (map[string]interface{}, error)
ValidateAllowMissingIatNbfExp validates JWTs of the JWS compact serialization form.
The given JWT is considered valid if:
- Its signature is successfully verified.
- Its claims set and header parameter values match what's given by Expected.
- It's valid with respect to the current time. This means that the current time must be within the times (inclusive) given by the "nbf" (Not Before) and "exp" (Expiration Time) claims and after the time given by the "iat" (Issued At) claim, with configurable leeway, if they are present. If all of "nbf", "exp", and "iat" are missing, then this check is skipped. See Expected.Now() for details on how the current time is provided for validation.