diff --git a/fosite/handler/openid/strategy_jwt.go b/fosite/handler/openid/strategy_jwt.go index 38b7e9fa89..f4bfb6f881 100644 --- a/fosite/handler/openid/strategy_jwt.go +++ b/fosite/handler/openid/strategy_jwt.go @@ -5,7 +5,9 @@ package openid import ( "context" + "slices" "strconv" + "strings" "time" "github.com/ory/x/errorsx" @@ -157,20 +159,20 @@ func (h DefaultStrategy) GenerateIDToken(ctx context.Context, lifespan time.Dura } } - prompt := requester.GetRequestForm().Get("prompt") - if prompt != "" { + prompts := fosite.RemoveEmpty(strings.Split(requester.GetRequestForm().Get("prompt"), " ")) + if len(prompts) > 0 { if claims.AuthTime.IsZero() { return "", errorsx.WithStack(fosite.ErrServerError.WithDebug("Unable to determine validity of prompt parameter because auth_time is missing in id token claims.")) } } - switch prompt { - case "none": + if slices.Contains(prompts, "none") { if !claims.AuthTime.Equal(claims.RequestedAt) && claims.AuthTime.After(claims.RequestedAt) { return "", errorsx.WithStack(fosite.ErrServerError. WithDebugf("Failed to generate id token because prompt was set to 'none' but auth_time ('%s') happened after the authorization request ('%s') was registered, indicating that the user was logged in during this request which is not allowed.", claims.AuthTime, claims.RequestedAt)) } - case "login": + } + if slices.Contains(prompts, "login") { if !claims.AuthTime.Equal(claims.RequestedAt) && claims.AuthTime.Before(claims.RequestedAt) { return "", errorsx.WithStack(fosite.ErrServerError. WithDebugf("Failed to generate id token because prompt was set to 'login' but auth_time ('%s') happened before the authorization request ('%s') was registered, indicating that the user was not re-authenticated which is forbidden.", claims.AuthTime, claims.RequestedAt)) diff --git a/fosite/handler/openid/strategy_jwt_test.go b/fosite/handler/openid/strategy_jwt_test.go index be0edd98fc..503410ffef 100644 --- a/fosite/handler/openid/strategy_jwt_test.go +++ b/fosite/handler/openid/strategy_jwt_test.go @@ -213,6 +213,36 @@ func TestJWTStrategy_GenerateIDToken(t *testing.T) { }, expectErr: true, }, + { + description: "should pass because prompt=select_account consent is valid space-separated per OIDC spec", + setup: func() { + req = fosite.NewAccessRequest(&openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{ + Subject: "peter", + AuthTime: time.Now().Add(-time.Hour).UTC(), + RequestedAt: time.Now().Add(-time.Minute), + }, + Headers: &jwt.Headers{}, + }) + req.Form.Set("prompt", "select_account consent") + }, + expectErr: false, + }, + { + description: "should fail because prompt includes login in space-separated list and auth_time indicates old login", + setup: func() { + req = fosite.NewAccessRequest(&openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{ + Subject: "peter", + AuthTime: time.Now().Add(-time.Hour).UTC(), + RequestedAt: time.Now().Add(-time.Minute), + }, + Headers: &jwt.Headers{}, + }) + req.Form.Set("prompt", "login consent") + }, + expectErr: true, + }, { description: "should pass because id_token_hint subject matches subject from claims", setup: func() { diff --git a/fosite/handler/openid/validator_test.go b/fosite/handler/openid/validator_test.go index dbdac9eec4..11cfcb5851 100644 --- a/fosite/handler/openid/validator_test.go +++ b/fosite/handler/openid/validator_test.go @@ -178,6 +178,34 @@ func TestValidatePrompt(t *testing.T) { }, }, }, + { + d: "should pass because select_account consent is a valid space-separated prompt per OIDC spec", + prompt: "select_account consent", + isPublic: false, + expectErr: false, + s: &openid.DefaultSession{ + Subject: "foo", + Claims: &jwt.IDTokenClaims{ + Subject: "foo", + RequestedAt: time.Now().UTC(), + AuthTime: time.Now().UTC().Add(-time.Minute), + }, + }, + }, + { + d: "should pass because select_account consent works with public clients", + prompt: "select_account consent", + isPublic: true, + expectErr: false, + s: &openid.DefaultSession{ + Subject: "foo", + Claims: &jwt.IDTokenClaims{ + Subject: "foo", + RequestedAt: time.Now().UTC(), + AuthTime: time.Now().UTC().Add(-time.Minute), + }, + }, + }, { d: "should pass because requesting consent and login works with confidential clients", prompt: "login consent",