Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

edgecontext: Use reddit's fork of jwt-go for key rotation #95

Merged
merged 1 commit into from
Feb 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 22 additions & 54 deletions edgecontext/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,13 @@ import (
"github.com/reddit/baseplate.go/secrets"
)

type keysType []*rsa.PublicKey
type keysType = []*rsa.PublicKey

const (
authenticationPubKeySecretPath = "secret/authentication/public-key"
jwtAlg = "RS256"
)

// When trying versioned secret with jwt, there are some errors that won't be
// fixed by the next version of the secret, so we can return early instead of
// trying all the remaining versions.
//
// TODO: We can also get rid of this block when upstream added native support
// for key rotation.
var shortCircuitErrors = []uint32{
jwt.ValidationErrorMalformed,
jwt.ValidationErrorAudience,
jwt.ValidationErrorExpired,
jwt.ValidationErrorIssuedAt,
jwt.ValidationErrorIssuer,
jwt.ValidationErrorNotValidYet,
jwt.ValidationErrorId,
jwt.ValidationErrorClaimsInvalid,
}

func shouldShortCircutError(err error) bool {
var ve jwt.ValidationError
if errors.As(err, &ve) {
for _, bitmask := range shortCircuitErrors {
if ve.Errors&bitmask != 0 {
return true
}
}
}
return false
}

// ValidateToken parses and validates a jwt token, and return the decoded
// AuthenticationToken.
func ValidateToken(token string) (*AuthenticationToken, error) {
Expand All @@ -55,33 +26,30 @@ func ValidateToken(token string) (*AuthenticationToken, error) {
return nil, errors.New("no public keys loaded")
}

// TODO: Patch upstream to support key rotation natively:
// https://github.com/dgrijalva/jwt-go/pull/372
var lastErr error
for _, key := range keys {
token, err := jwt.ParseWithClaims(
token,
&AuthenticationToken{},
func(_ *jwt.Token) (interface{}, error) {
return key, nil
},
)
if err != nil {
if shouldShortCircutError(err) {
return nil, err
}
// Try next pubkey.
lastErr = err
continue
}
tok, err := jwt.ParseWithClaims(
token,
&AuthenticationToken{},
func(_ *jwt.Token) (interface{}, error) {
return keys, nil
},
)
if err != nil {
return nil, err
}

if claims, ok := token.Claims.(*AuthenticationToken); ok && token.Valid && token.Method.Alg() == jwtAlg {
return claims, nil
}
if !tok.Valid {
return nil, jwt.NewValidationError("invalid token", 0)
}

if tok.Method.Alg() != jwtAlg {
return nil, jwt.NewValidationError("wrong signing method", 0)
}

lastErr = jwt.NewValidationError("", 0)
if claims, ok := tok.Claims.(*AuthenticationToken); ok {
return claims, nil
}
return nil, lastErr

return nil, jwt.NewValidationError("invalid token type", 0)
}

func validatorMiddleware(next secrets.SecretHandlerFunc) secrets.SecretHandlerFunc {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ require (
gopkg.in/dgrijalva/jwt-go.v3 v3.2.0
gopkg.in/fsnotify.v1 v1.4.7
)

replace gopkg.in/dgrijalva/jwt-go.v3 => github.com/reddit/jwt-go v3.2.1-0.20200222044038-a63f2d40479f+incompatible
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/reddit/jwt-go v3.2.1-0.20200222044038-a63f2d40479f+incompatible h1:d2fV4H2zMs1kC0dw5N9qbsWW45SsRQSta8IlWEwAG4g=
github.com/reddit/jwt-go v3.2.1-0.20200222044038-a63f2d40479f+incompatible/go.mod h1:DnRZZdtPlHMhfOZTDM2U49R+PsC3qEV0E+y6rr7Od3o=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand Down Expand Up @@ -95,8 +97,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/dgrijalva/jwt-go.v3 v3.2.0 h1:N46iQqOtHry7Hxzb9PGrP68oovQmj7EhudNoKHvbOvI=
gopkg.in/dgrijalva/jwt-go.v3 v3.2.0/go.mod h1:hdNXC2Z9yC029rvsQ/on2ZNQ44Z2XToVhpXXbR+J05A=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
Expand Down