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

fix: jwt strategy retains original iat and nbf claims #21

Merged
merged 1 commit into from
Dec 22, 2023
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ following list of differences:
request
- [ ] Response Type None
- [ ] Client Secret Validation Interface
- [ ] Clock Drift Support
- [ ] Injectable Clock Configurator
- [ ] Support `s_hash`
- Removal of the following dependencies:
- [x] `go.opentelemetry.io/otel/trace`
- [x] `github.com/ecordell/optgen`
Expand Down
2 changes: 2 additions & 0 deletions handler/oauth2/strategy_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,14 @@ func (h *DefaultJWTStrategy) generate(ctx context.Context, tokenType oauth2.Toke
return "", "", errors.New("GetTokenClaims() must not be nil")
} else {
claims := jwtSession.GetJWTClaims().
Sanitize().
With(
jwtSession.GetExpiresAt(tokenType),
requester.GetGrantedScopes(),
requester.GetGrantedAudience(),
).
WithDefaults(
time.Now().UTC(),
time.Now().UTC(),
h.Config.GetAccessTokenIssuer(ctx),
).
Expand Down
13 changes: 10 additions & 3 deletions handler/oauth2/strategy_jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ var jwtExpiredCase = func(tokenType oauth2.TokenType) *oauth2.Request {
JWTClaims: &jwt.JWTClaims{
Issuer: "oauth2",
Subject: "peter",
IssuedAt: time.Now().UTC(),
NotBefore: time.Now().UTC(),
IssuedAt: time.Now().UTC().Add(-time.Minute * 10),
NotBefore: time.Now().UTC().Add(-time.Minute * 10),
ExpiresAt: time.Now().UTC().Add(-time.Minute),
Extra: map[string]any{"foo": "bar"},
},
Expand Down Expand Up @@ -188,7 +188,7 @@ func TestAccessToken(t *testing.T) {
j.Config = &oauth2.Config{
JWTScopeClaimKey: scopeField,
}
token, signature, err := j.GenerateAccessToken(context.Background(), c.r)
token, signature, err := j.GenerateAccessToken(context.TODO(), c.r)
assert.NoError(t, err)

parts := strings.Split(token, ".")
Expand Down Expand Up @@ -231,6 +231,9 @@ func TestAccessToken(t *testing.T) {
// Scope field is always a string.
assert.Equal(t, "email offline", claims[consts.ClaimScope])

assert.WithinDuration(t, time.Now(), anyInt64ToTime(claims[consts.ClaimIssuedAt]), time.Second)
assert.WithinDuration(t, time.Now(), anyInt64ToTime(claims[consts.ClaimNotBefore]), time.Second)

validate := j.signature(token)
err = j.ValidateAccessToken(context.Background(), c.r, token)
if c.pass {
Expand All @@ -243,3 +246,7 @@ func TestAccessToken(t *testing.T) {
}
}
}

func anyInt64ToTime(in any) time.Time {
return time.Unix(in.(int64), 0)
}
18 changes: 16 additions & 2 deletions token/jwt/claims_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ type JWTClaimsDefaults struct {
}

type JWTClaimsContainer interface {
// Sanitize should clear the IssuedAt and NotBefore values.
Sanitize() JWTClaimsContainer

// With returns a copy of itself with expiresAt, scope, audience set to the given values.
With(expiry time.Time, scope, audience []string) JWTClaimsContainer

// WithDefaults returns a copy of itself with issuedAt and issuer set to the given default values. If those
// values are already set in the claims, they will not be updated.
WithDefaults(iat time.Time, issuer string) JWTClaimsContainer
WithDefaults(iat, nbf time.Time, issuer string) JWTClaimsContainer

// WithScopeField configures how a scope field should be represented in JWT.
WithScopeField(scopeField JWTScopeFieldEnum) JWTClaimsContainer
Expand Down Expand Up @@ -65,11 +68,22 @@ func (c *JWTClaims) With(expiry time.Time, scope, audience []string) JWTClaimsCo
return c
}

func (c *JWTClaims) WithDefaults(iat time.Time, issuer string) JWTClaimsContainer {
func (c *JWTClaims) Sanitize() JWTClaimsContainer {
c.IssuedAt = time.Time{}
c.NotBefore = time.Time{}

return c
}

func (c *JWTClaims) WithDefaults(iat, nbf time.Time, issuer string) JWTClaimsContainer {
if c.IssuedAt.IsZero() {
c.IssuedAt = iat
}

if c.NotBefore.IsZero() {
c.NotBefore = nbf
}

if c.Issuer == "" {
c.Issuer = issuer
}
Expand Down