go.mod: github.com/Azure/go-autorest/autorest v0.11.24

Update the indirect dependency to remove the transitional github.com/form3tech-oss/jwt-go
dependency from the dependency graph.

Updates:

- github.com/Azure/go-autorest/autorest v0.11.24: https://github.com/Azure/go-autorest/compare/autorest/v0.11.20...autorest/v0.11.24
- github.com/Azure/go-autorest/autorest/adal v0.9.18: https://github.com/Azure/go-autorest/compare/autorest/adal/v0.9.15...autorest/adal/v0.9.18
- github.com/golang-jwt/jwt v4.2.0: https://github.com/golang-jwt/jwt/compare/v4.0.0...v4.2.0
- golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3: 32db794688...e495a2d5b3

Before this:

    go mod graph | grep 'jwt'
    github.com/Azure/go-autorest/autorest/adal@v0.9.15 github.com/golang-jwt/jwt/v4@v4.0.0
    github.com/Azure/go-autorest/autorest/adal@v0.9.13 github.com/form3tech-oss/jwt-go@v3.2.2+incompatible

After this:

    go mod graph | grep 'jwt'
    github.com/Azure/go-autorest/autorest@v0.11.24 github.com/golang-jwt/jwt/v4@v4.2.0
    github.com/Azure/go-autorest/autorest/adal@v0.9.18 github.com/golang-jwt/jwt/v4@v4.0.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-01-24 11:30:22 +01:00
parent be4c921514
commit 4f1c1e4268
No known key found for this signature in database
GPG key ID: 76698F39D527CE8C
28 changed files with 587 additions and 170 deletions

View file

@ -1,6 +1,6 @@
## Migration Guide (v4.0.0)
Starting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0]), the import path will be:
Starting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0), the import path will be:
"github.com/golang-jwt/jwt/v4"
@ -19,4 +19,4 @@ go mod tidy
## Older releases (before v3.2.0)
The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md.
The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md.

View file

@ -1,12 +1,12 @@
# jwt-go
[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt)
[![Go Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt/v4.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt/v4)
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519).
Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0) this project adds Go module support, but maintains backwards compataibility with older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`.
See the `MIGRATION_GUIDE.md` for more information.
Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0) this project adds Go module support, but maintains backwards compatibility with older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`.
See the [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information.
> After the original author of the library suggested migrating the maintenance of `jwt-go`, a dedicated team of open source maintainers decided to clone the existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic.
@ -52,7 +52,7 @@ Here's an example of an extension that integrates with multiple Google Cloud Pla
## Compliance
This library was last reviewed to comply with [RTF 7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few notable differences:
This library was last reviewed to comply with [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few notable differences:
* In order to protect against accidental use of [Unsecured JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key.
@ -91,6 +91,7 @@ Each signing method expects a different object type for its signing keys. See th
* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation
* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation
* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation
* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation
### JWT and OAuth

View file

@ -12,9 +12,122 @@ type Claims interface {
Valid() error
}
// StandardClaims are a structured version of the Claims Section, as referenced at
// https://tools.ietf.org/html/rfc7519#section-4.1
// See examples for how to use this with your own claim types
// RegisteredClaims are a structured version of the JWT Claims Set,
// restricted to Registered Claim Names, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
//
// This type can be used on its own, but then additional private and
// public claims embedded in the JWT will not be parsed. The typical usecase
// therefore is to embedded this in a user-defined claim type.
//
// See examples for how to use this with your own claim types.
type RegisteredClaims struct {
// the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
Issuer string `json:"iss,omitempty"`
// the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2
Subject string `json:"sub,omitempty"`
// the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
Audience ClaimStrings `json:"aud,omitempty"`
// the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4
ExpiresAt *NumericDate `json:"exp,omitempty"`
// the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5
NotBefore *NumericDate `json:"nbf,omitempty"`
// the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6
IssuedAt *NumericDate `json:"iat,omitempty"`
// the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7
ID string `json:"jti,omitempty"`
}
// Valid validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (c RegisteredClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc()
// The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them.
if !c.VerifyExpiresAt(now, false) {
delta := now.Sub(c.ExpiresAt.Time)
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
vErr.Errors |= ValidationErrorExpired
}
if !c.VerifyIssuedAt(now, false) {
vErr.Inner = fmt.Errorf("token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if !c.VerifyNotBefore(now, false) {
vErr.Inner = fmt.Errorf("token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}
// VerifyAudience compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud(c.Audience, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool) bool {
if c.ExpiresAt == nil {
return verifyExp(nil, cmp, req)
}
return verifyExp(&c.ExpiresAt.Time, cmp, req)
}
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
if c.IssuedAt == nil {
return verifyIat(nil, cmp, req)
}
return verifyIat(&c.IssuedAt.Time, cmp, req)
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool) bool {
if c.NotBefore == nil {
return verifyNbf(nil, cmp, req)
}
return verifyNbf(&c.NotBefore.Time, cmp, req)
}
// VerifyIssuer compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *RegisteredClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
// StandardClaims are a structured version of the JWT Claims Set, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-4. They do not follow the
// specification exactly, since they were based on an earlier draft of the
// specification and not updated. The main difference is that they only
// support integer-based date fields and singular audiences. This might lead to
// incompatibilities with other JWT implementations. The use of this is discouraged, instead
// the newer RegisteredClaims struct should be used.
//
// Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct.
type StandardClaims struct {
Audience string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
@ -41,7 +154,7 @@ func (c StandardClaims) Valid() error {
}
if !c.VerifyIssuedAt(now, false) {
vErr.Inner = fmt.Errorf("Token used before issued")
vErr.Inner = fmt.Errorf("token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
@ -63,16 +176,37 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud([]string{c.Audience}, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
return verifyExp(c.ExpiresAt, cmp, req)
if c.ExpiresAt == 0 {
return verifyExp(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.ExpiresAt, 0)
return verifyExp(&t, time.Unix(cmp, 0), req)
}
// VerifyIssuedAt compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
return verifyIat(c.IssuedAt, cmp, req)
if c.IssuedAt == 0 {
return verifyIat(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.IssuedAt, 0)
return verifyIat(&t, time.Unix(cmp, 0), req)
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
if c.NotBefore == 0 {
return verifyNbf(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.NotBefore, 0)
return verifyNbf(&t, time.Unix(cmp, 0), req)
}
// VerifyIssuer compares the iss claim against cmp.
@ -81,12 +215,6 @@ func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
// VerifyNotBefore compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
return verifyNbf(c.NotBefore, cmp, req)
}
// ----- helpers
func verifyAud(aud []string, cmp string, required bool) bool {
@ -112,18 +240,25 @@ func verifyAud(aud []string, cmp string, required bool) bool {
return result
}
func verifyExp(exp int64, now int64, required bool) bool {
if exp == 0 {
func verifyExp(exp *time.Time, now time.Time, required bool) bool {
if exp == nil {
return !required
}
return now <= exp
return now.Before(*exp)
}
func verifyIat(iat int64, now int64, required bool) bool {
if iat == 0 {
func verifyIat(iat *time.Time, now time.Time, required bool) bool {
if iat == nil {
return !required
}
return now >= iat
return now.After(*iat) || now.Equal(*iat)
}
func verifyNbf(nbf *time.Time, now time.Time, required bool) bool {
if nbf == nil {
return !required
}
return now.After(*nbf) || now.Equal(*nbf)
}
func verifyIss(iss string, cmp string, required bool) bool {
@ -136,10 +271,3 @@ func verifyIss(iss string, cmp string, required bool) bool {
return false
}
}
func verifyNbf(nbf int64, now int64, required bool) bool {
if nbf == 0 {
return !required
}
return now >= nbf
}

View file

@ -3,7 +3,9 @@ package jwt
import (
"errors"
"crypto"
"crypto/ed25519"
"crypto/rand"
)
var (
@ -62,20 +64,22 @@ func (m *SigningMethodEd25519) Verify(signingString, signature string, key inter
// Sign implements token signing for the SigningMethod.
// For this signing method, key must be an ed25519.PrivateKey
func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) (string, error) {
var ed25519Key ed25519.PrivateKey
var ed25519Key crypto.Signer
var ok bool
if ed25519Key, ok = key.(ed25519.PrivateKey); !ok {
if ed25519Key, ok = key.(crypto.Signer); !ok {
return "", ErrInvalidKeyType
}
// ed25519.Sign panics if private key not equal to ed25519.PrivateKeySize
// this allows to avoid recover usage
if len(ed25519Key) != ed25519.PrivateKeySize {
if _, ok := ed25519Key.Public().(ed25519.PublicKey); !ok {
return "", ErrInvalidKey
}
// Sign the string and return the encoded result
sig := ed25519.Sign(ed25519Key, []byte(signingString))
// ed25519 performs a two-pass hash as part of its algorithm. Therefore, we need to pass a non-prehashed message into the Sign function, as indicated by crypto.Hash(0)
sig, err := ed25519Key.Sign(rand.Reader, []byte(signingString), crypto.Hash(0))
if err != nil {
return "", err
}
return EncodeSegment(sig), nil
}

View file

@ -53,6 +53,11 @@ func (e ValidationError) Error() string {
}
}
// Unwrap gives errors.Is and errors.As access to the inner error.
func (e *ValidationError) Unwrap() error {
return e.Inner
}
// No errors
func (e *ValidationError) valid() bool {
return e.Errors == 0

View file

@ -3,6 +3,7 @@ package jwt
import (
"encoding/json"
"errors"
"time"
// "fmt"
)
@ -31,37 +32,81 @@ func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud(aud, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
// VerifyExpiresAt compares the exp claim against cmp (cmp <= exp).
// If req is false, it will return true, if exp is unset.
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
exp, ok := m["exp"]
cmpTime := time.Unix(cmp, 0)
v, ok := m["exp"]
if !ok {
return !req
}
switch expType := exp.(type) {
switch exp := v.(type) {
case float64:
return verifyExp(int64(expType), cmp, req)
if exp == 0 {
return verifyExp(nil, cmpTime, req)
}
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req)
case json.Number:
v, _ := expType.Int64()
return verifyExp(v, cmp, req)
v, _ := exp.Float64()
return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req)
}
return false
}
// VerifyIssuedAt compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
// VerifyIssuedAt compares the exp claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
iat, ok := m["iat"]
cmpTime := time.Unix(cmp, 0)
v, ok := m["iat"]
if !ok {
return !req
}
switch iatType := iat.(type) {
switch iat := v.(type) {
case float64:
return verifyIat(int64(iatType), cmp, req)
if iat == 0 {
return verifyIat(nil, cmpTime, req)
}
return verifyIat(&newNumericDateFromSeconds(iat).Time, cmpTime, req)
case json.Number:
v, _ := iatType.Int64()
return verifyIat(v, cmp, req)
v, _ := iat.Float64()
return verifyIat(&newNumericDateFromSeconds(v).Time, cmpTime, req)
}
return false
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
cmpTime := time.Unix(cmp, 0)
v, ok := m["nbf"]
if !ok {
return !req
}
switch nbf := v.(type) {
case float64:
if nbf == 0 {
return verifyNbf(nil, cmpTime, req)
}
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req)
case json.Number:
v, _ := nbf.Float64()
return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req)
}
return false
}
@ -72,24 +117,7 @@ func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(iss, cmp, req)
}
// VerifyNotBefore compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
nbf, ok := m["nbf"]
if !ok {
return !req
}
switch nbfType := nbf.(type) {
case float64:
return verifyNbf(int64(nbfType), cmp, req)
case json.Number:
v, _ := nbfType.Int64()
return verifyNbf(v, cmp, req)
}
return false
}
// Valid calidates time based claims "exp, iat, nbf".
// Valid validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.

View file

@ -8,14 +8,36 @@ import (
)
type Parser struct {
ValidMethods []string // If populated, only these methods will be considered valid
UseJSONNumber bool // Use JSON Number format in JSON decoder
SkipClaimsValidation bool // Skip claims validation during token parsing
// If populated, only these methods will be considered valid.
//
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
ValidMethods []string
// Use JSON Number format in JSON decoder.
//
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
UseJSONNumber bool
// Skip claims validation during token parsing.
//
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
SkipClaimsValidation bool
}
// Parse parses, validates, and returns a token.
// NewParser creates a new Parser with the specified options
func NewParser(options ...ParserOption) *Parser {
p := &Parser{}
// loop through our parsing options and apply them
for _, option := range options {
option(p)
}
return p
}
// Parse parses, validates, verifies the signature and returns the parsed token.
// keyFunc will receive the parsed token and should return the key for validating.
// If everything is kosher, err will be nil
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
}

29
vendor/github.com/golang-jwt/jwt/v4/parser_option.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
package jwt
// ParserOption is used to implement functional-style options that modify the behaviour of the parser. To add
// new options, just create a function (ideally beginning with With or Without) that returns an anonymous function that
// takes a *Parser type as input and manipulates its configuration accordingly.
type ParserOption func(*Parser)
// WithValidMethods is an option to supply algorithm methods that the parser will check. Only those methods will be considered valid.
// It is heavily encouraged to use this option in order to prevent attacks such as https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/.
func WithValidMethods(methods []string) ParserOption {
return func(p *Parser) {
p.ValidMethods = methods
}
}
// WithJSONNumber is an option to configure the underyling JSON parser with UseNumber
func WithJSONNumber() ParserOption {
return func(p *Parser) {
p.UseJSONNumber = true
}
}
// WithoutClaimsValidation is an option to disable claims validation. This option should only be used if you exactly know
// what you are doing.
func WithoutClaimsValidation() ParserOption {
return func(p *Parser) {
p.SkipClaimsValidation = true
}
}

View file

@ -33,3 +33,14 @@ func GetSigningMethod(alg string) (method SigningMethod) {
}
return
}
// GetAlgorithms returns a list of registered "alg" names
func GetAlgorithms() (algs []string) {
signingMethodLock.RLock()
defer signingMethodLock.RUnlock()
for alg := range signingMethods {
algs = append(algs, alg)
}
return
}

View file

@ -7,6 +7,14 @@ import (
"time"
)
// DecodePaddingAllowed will switch the codec used for decoding JWTs respectively. Note that the JWS RFC7515
// states that the tokens will utilize a Base64url encoding with no padding. Unfortunately, some implementations
// of JWT are producing non-standard tokens, and thus require support for decoding. Note that this is a global
// variable, and updating it will change the behavior on a package level, and is also NOT go-routine safe.
// To use the non-recommended decoding, set this boolean to `true` prior to using this package.
var DecodePaddingAllowed bool
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
// You can override it to use another time value. This is useful for testing or if your
// server uses a different time zone than your tokens.
@ -29,11 +37,12 @@ type Token struct {
Valid bool // Is the token valid? Populated when you Parse/Verify a token
}
// New creates a new Token. Takes a signing method
// New creates a new Token with the specified signing method and an empty map of claims.
func New(method SigningMethod) *Token {
return NewWithClaims(method, MapClaims{})
}
// NewWithClaims creates a new Token with the specified signing method and claims.
func NewWithClaims(method SigningMethod, claims Claims) *Token {
return &Token{
Header: map[string]interface{}{
@ -45,7 +54,8 @@ func NewWithClaims(method SigningMethod, claims Claims) *Token {
}
}
// SignedString retrieves the complete, signed token
// SignedString creates and returns a complete, signed JWT.
// The token is signed using the SigningMethod specified in the token.
func (t *Token) SignedString(key interface{}) (string, error) {
var sig, sstr string
var err error
@ -82,15 +92,19 @@ func (t *Token) SigningString() (string, error) {
return strings.Join(parts, "."), nil
}
// Parse parses, validates, and returns a token.
// keyFunc will receive the parsed token and should return the key for validating.
// If everything is kosher, err will be nil
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return new(Parser).Parse(tokenString, keyFunc)
// Parse parses, validates, verifies the signature and returns the parsed token.
// keyFunc will receive the parsed token and should return the cryptographic key
// for verifying the signature.
// The caller is strongly encouraged to set the WithValidMethods option to
// validate the 'alg' claim in the token matches the expected algorithm.
// For more details about the importance of validating the 'alg' claim,
// see https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {
return NewParser(options...).Parse(tokenString, keyFunc)
}
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
return new(Parser).ParseWithClaims(tokenString, claims, keyFunc)
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {
return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc)
}
// EncodeSegment encodes a JWT specific base64url encoding with padding stripped
@ -106,5 +120,12 @@ func EncodeSegment(seg []byte) string {
// Deprecated: In a future release, we will demote this function to a non-exported function, since it
// should only be used internally
func DecodeSegment(seg string) ([]byte, error) {
if DecodePaddingAllowed {
if l := len(seg) % 4; l > 0 {
seg += strings.Repeat("=", 4-l)
}
return base64.URLEncoding.DecodeString(seg)
}
return base64.RawURLEncoding.DecodeString(seg)
}

127
vendor/github.com/golang-jwt/jwt/v4/types.go generated vendored Normal file
View file

@ -0,0 +1,127 @@
package jwt
import (
"encoding/json"
"fmt"
"math"
"reflect"
"strconv"
"time"
)
// TimePrecision sets the precision of times and dates within this library.
// This has an influence on the precision of times when comparing expiry or
// other related time fields. Furthermore, it is also the precision of times
// when serializing.
//
// For backwards compatibility the default precision is set to seconds, so that
// no fractional timestamps are generated.
var TimePrecision = time.Second
// MarshalSingleStringAsArray modifies the behaviour of the ClaimStrings type, especially
// its MarshalJSON function.
//
// If it is set to true (the default), it will always serialize the type as an
// array of strings, even if it just contains one element, defaulting to the behaviour
// of the underlying []string. If it is set to false, it will serialize to a single
// string, if it contains one element. Otherwise, it will serialize to an array of strings.
var MarshalSingleStringAsArray = true
// NumericDate represents a JSON numeric date value, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-2.
type NumericDate struct {
time.Time
}
// NewNumericDate constructs a new *NumericDate from a standard library time.Time struct.
// It will truncate the timestamp according to the precision specified in TimePrecision.
func NewNumericDate(t time.Time) *NumericDate {
return &NumericDate{t.Truncate(TimePrecision)}
}
// newNumericDateFromSeconds creates a new *NumericDate out of a float64 representing a
// UNIX epoch with the float fraction representing non-integer seconds.
func newNumericDateFromSeconds(f float64) *NumericDate {
round, frac := math.Modf(f)
return NewNumericDate(time.Unix(int64(round), int64(frac*1e9)))
}
// MarshalJSON is an implementation of the json.RawMessage interface and serializes the UNIX epoch
// represented in NumericDate to a byte array, using the precision specified in TimePrecision.
func (date NumericDate) MarshalJSON() (b []byte, err error) {
f := float64(date.Truncate(TimePrecision).UnixNano()) / float64(time.Second)
return []byte(strconv.FormatFloat(f, 'f', -1, 64)), nil
}
// UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a
// NumericDate from a JSON representation, i.e. a json.Number. This number represents an UNIX epoch
// with either integer or non-integer seconds.
func (date *NumericDate) UnmarshalJSON(b []byte) (err error) {
var (
number json.Number
f float64
)
if err = json.Unmarshal(b, &number); err != nil {
return fmt.Errorf("could not parse NumericData: %w", err)
}
if f, err = number.Float64(); err != nil {
return fmt.Errorf("could not convert json number value to float: %w", err)
}
n := newNumericDateFromSeconds(f)
*date = *n
return nil
}
// ClaimStrings is basically just a slice of strings, but it can be either serialized from a string array or just a string.
// This type is necessary, since the "aud" claim can either be a single string or an array.
type ClaimStrings []string
func (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) {
var value interface{}
if err = json.Unmarshal(data, &value); err != nil {
return err
}
var aud []string
switch v := value.(type) {
case string:
aud = append(aud, v)
case []string:
aud = ClaimStrings(v)
case []interface{}:
for _, vv := range v {
vs, ok := vv.(string)
if !ok {
return &json.UnsupportedTypeError{Type: reflect.TypeOf(vv)}
}
aud = append(aud, vs)
}
case nil:
return nil
default:
return &json.UnsupportedTypeError{Type: reflect.TypeOf(v)}
}
*s = aud
return
}
func (s ClaimStrings) MarshalJSON() (b []byte, err error) {
// This handles a special case in the JWT RFC. If the string array, e.g. used by the "aud" field,
// only contains one element, it MAY be serialized as a single string. This may or may not be
// desired based on the ecosystem of other JWT library used, so we make it configurable by the
// variable MarshalSingleStringAsArray.
if len(s) == 1 && !MarshalSingleStringAsArray {
return json.Marshal(s[0])
}
return json.Marshal([]string(s))
}