Skip to content

Commit f77fa56

Browse files
committed
Honour nbf claim if present in ID token
1 parent 274971e commit f77fa56

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

oidc.go

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ type idToken struct {
321321
Audience audience `json:"aud"`
322322
Expiry jsonTime `json:"exp"`
323323
IssuedAt jsonTime `json:"iat"`
324+
NotBefore *jsonTime `json:"nbf"`
324325
Nonce string `json:"nonce"`
325326
AtHash string `json:"at_hash"`
326327
ClaimNames map[string]string `json:"_claim_names"`

verify.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,21 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
270270
if v.config.Now != nil {
271271
now = v.config.Now
272272
}
273+
nowTime := now()
273274

274-
if t.Expiry.Before(now()) {
275+
if t.Expiry.Before(nowTime) {
275276
return nil, fmt.Errorf("oidc: token is expired (Token Expiry: %v)", t.Expiry)
276277
}
278+
279+
// If nbf claim is provided in token, ensure that it is indeed in the past.
280+
if token.NotBefore != nil {
281+
nbfTime := time.Time(*token.NotBefore)
282+
leeway := 1 * time.Minute
283+
284+
if nowTime.Add(leeway).Before(nbfTime) {
285+
return nil, fmt.Errorf("oidc: current time %v before the nbf (not before) time: %v", nowTime, nbfTime)
286+
}
287+
}
277288
}
278289

279290
switch len(jws.Signatures) {

verify_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,34 @@ func TestVerify(t *testing.T) {
107107
},
108108
signKey: newRSAKey(t),
109109
},
110+
{
111+
name: "nbf in future",
112+
idToken: `{"iss":"https://foo","nbf":` + strconv.FormatInt(time.Now().Add(time.Hour).Unix(), 10) +
113+
`,"exp":` + strconv.FormatInt(time.Now().Add(time.Hour).Unix(), 10) + `}`,
114+
config: Config{
115+
SkipClientIDCheck: true,
116+
},
117+
signKey: newRSAKey(t),
118+
wantErr: true,
119+
},
120+
{
121+
name: "nbf in past",
122+
idToken: `{"iss":"https://foo","nbf":` + strconv.FormatInt(time.Now().Add(-time.Hour).Unix(), 10) +
123+
`,"exp":` + strconv.FormatInt(time.Now().Add(time.Hour).Unix(), 10) + `}`,
124+
config: Config{
125+
SkipClientIDCheck: true,
126+
},
127+
signKey: newRSAKey(t),
128+
},
129+
{
130+
name: "nbf in future within clock skew tolerance",
131+
idToken: `{"iss":"https://foo","nbf":` + strconv.FormatInt(time.Now().Add(30*time.Second).Unix(), 10) +
132+
`,"exp":` + strconv.FormatInt(time.Now().Add(time.Hour).Unix(), 10) + `}`,
133+
config: Config{
134+
SkipClientIDCheck: true,
135+
},
136+
signKey: newRSAKey(t),
137+
},
110138
}
111139
for _, test := range tests {
112140
t.Run(test.name, test.run)

0 commit comments

Comments
 (0)