oidc refresh token: validate nonce only if set

As clarified in OpenID core spec errata 2, section 12.2

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-07-01 19:06:11 +02:00
parent 55be9f0b9c
commit 64a2f7aa4f
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
2 changed files with 10 additions and 6 deletions

View file

@ -16,6 +16,7 @@ package httpd
import ( import (
"context" "context"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -203,7 +204,7 @@ type oidcPendingAuth struct {
func newOIDCPendingAuth(audience tokenAudience) oidcPendingAuth { func newOIDCPendingAuth(audience tokenAudience) oidcPendingAuth {
return oidcPendingAuth{ return oidcPendingAuth{
State: xid.New().String(), State: xid.New().String(),
Nonce: xid.New().String(), Nonce: hex.EncodeToString(util.GenerateRandomBytes(20)),
Audience: audience, Audience: audience,
IssuedAt: util.GetTimeAsMsSinceEpoch(time.Now()), IssuedAt: util.GetTimeAsMsSinceEpoch(time.Now()),
} }
@ -345,14 +346,15 @@ func (t *oidcToken) refresh(ctx context.Context, config OAuth2Config, verifier O
logger.Debug(logSender, "", "unable to verify refreshed id token for cookie %q: %v", t.Cookie, err) logger.Debug(logSender, "", "unable to verify refreshed id token for cookie %q: %v", t.Cookie, err)
return err return err
} }
if idToken.Nonce != t.Nonce { if idToken.Nonce != "" && idToken.Nonce != t.Nonce {
logger.Debug(logSender, "", "unable to verify refreshed id token for cookie %q: nonce mismatch", t.Cookie) logger.Warn(logSender, "", "unable to verify refreshed id token for cookie %q: nonce mismatch, expected: %q, actual: %q",
t.Cookie, t.Nonce, idToken.Nonce)
return errors.New("the refreshed token nonce mismatch") return errors.New("the refreshed token nonce mismatch")
} }
claims := make(map[string]any) claims := make(map[string]any)
err = idToken.Claims(&claims) err = idToken.Claims(&claims)
if err != nil { if err != nil {
logger.Debug(logSender, "", "unable to get refreshed id token claims for cookie %q: %v", t.Cookie, err) logger.Warn(logSender, "", "unable to get refreshed id token claims for cookie %q: %v", t.Cookie, err)
return err return err
} }
sid, ok := claims["sid"].(string) sid, ok := claims["sid"].(string)

View file

@ -626,7 +626,9 @@ func TestOIDCRefreshToken(t *testing.T) {
}, },
} }
verifier = mockOIDCVerifier{ verifier = mockOIDCVerifier{
token: &oidc.IDToken{}, token: &oidc.IDToken{
Nonce: xid.New().String(), // nonce is different from the expected one
},
} }
err = token.refresh(context.Background(), &config, &verifier, r) err = token.refresh(context.Background(), &config, &verifier, r)
if assert.Error(t, err) { if assert.Error(t, err) {
@ -634,7 +636,7 @@ func TestOIDCRefreshToken(t *testing.T) {
} }
verifier = mockOIDCVerifier{ verifier = mockOIDCVerifier{
token: &oidc.IDToken{ token: &oidc.IDToken{
Nonce: token.Nonce, Nonce: "", // empty token is fine on refresh but claims are not set
}, },
} }
err = token.refresh(context.Background(), &config, &verifier, r) err = token.refresh(context.Background(), &config, &verifier, r)