diff --git a/Makefile b/Makefile index 971a72e9902..19e8b0d19c7 100644 --- a/Makefile +++ b/Makefile @@ -93,7 +93,7 @@ quicktest-hsm: .PHONY: test-refresh test-refresh: - UPDATE_SNAPSHOTS=true go test -failfast -short -tags sqlite,sqlite_omit_load_extension ./... + UPDATE_SNAPSHOTS=true go test -short -tags sqlite,sqlite_omit_load_extension ./... DOCKER_CONTENT_TRUST=1 docker build --progress=plain -f .docker/Dockerfile-test-hsm --target test-refresh-hsm -t oryd/hydra:${IMAGE_TAG} --target test-refresh-hsm . authors: # updates the AUTHORS file diff --git a/consent/handler.go b/consent/handler.go index 701bb546931..b7af2d49afb 100644 --- a/consent/handler.go +++ b/consent/handler.go @@ -88,12 +88,12 @@ type revokeOAuth2ConsentSessions struct { // in: query Client string `json:"client"` - // Consent Challenge ID + // Consent Request ID // // If set, revoke all token chains derived from this particular consent request ID. // // in: query - ConsentChallengeID string `json:"consent_challenge_id"` + ConsentRequestID string `json:"consent_request_id"` // Revoke All Consent Sessions // @@ -122,44 +122,37 @@ type revokeOAuth2ConsentSessions struct { // 204: emptyResponse // default: errorOAuth2 func (h *Handler) revokeOAuth2ConsentSessions(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - subject := r.URL.Query().Get("subject") - client := r.URL.Query().Get("client") - consentChallengeID := r.URL.Query().Get("consent_challenge_id") - allClients := r.URL.Query().Get("all") == "true" - if subject == "" && consentChallengeID == "" { - h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter 'subject' or 'consent_challenge_id' are required.`))) - return - } - if consentChallengeID != "" && subject != "" { - h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter 'subject' and 'consent_challenge_id' cannot be set at the same time.`))) - return - } - if consentChallengeID != "" && client != "" { - h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter 'client' and 'consent_challenge_id' cannot be set at the same time.`))) - return - } + var ( + subject = r.URL.Query().Get("subject") + client = r.URL.Query().Get("client") + consentRequestID = r.URL.Query().Get("consent_request_id") + allClients = r.URL.Query().Get("all") == "true" + ) switch { - case client != "": + case consentRequestID != "" && subject == "" && client == "": + if err := h.r.ConsentManager().RevokeConsentSessionByID(r.Context(), consentRequestID); err != nil && !errors.Is(err, x.ErrNotFound) { + h.r.Writer().WriteError(w, r, err) + return + } + events.Trace(r.Context(), events.ConsentRevoked, events.WithConsentRequestID(consentRequestID)) + + case consentRequestID == "" && subject != "" && client != "" && !allClients: if err := h.r.ConsentManager().RevokeSubjectClientConsentSession(r.Context(), subject, client); err != nil && !errors.Is(err, x.ErrNotFound) { h.r.Writer().WriteError(w, r, err) return } events.Trace(r.Context(), events.ConsentRevoked, events.WithSubject(subject), events.WithClientID(client)) - case allClients: + + case consentRequestID == "" && subject != "" && client == "" && allClients: if err := h.r.ConsentManager().RevokeSubjectConsentSession(r.Context(), subject); err != nil && !errors.Is(err, x.ErrNotFound) { h.r.Writer().WriteError(w, r, err) return } events.Trace(r.Context(), events.ConsentRevoked, events.WithSubject(subject)) - case consentChallengeID != "": - if err := h.r.ConsentManager().RevokeConsentSessionByID(r.Context(), consentChallengeID); err != nil && !errors.Is(err, x.ErrNotFound) { - h.r.Writer().WriteError(w, r, err) - return - } - return + default: - h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter both 'client' and 'all' is not defined but one of them should have been.`))) + h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("Invalid combination of query parameters."))) return } @@ -782,7 +775,7 @@ func (h *Handler) acceptOAuth2ConsentRequest(w http.ResponseWriter, r *http.Requ return } - p.ID = challenge + p.ConsentRequestID = cr.ConsentRequestID p.RequestedAt = cr.RequestedAt p.HandledAt = sqlxx.NullTime(time.Now().UTC()) @@ -899,12 +892,13 @@ func (h *Handler) rejectOAuth2ConsentRequest(w http.ResponseWriter, r *http.Requ h.r.Writer().WriteError(w, r, err) return } + cr := f.GetConsentRequest(challenge) request, err := h.r.ConsentManager().HandleConsentRequest(ctx, f, &flow.AcceptOAuth2ConsentRequest{ - Error: &p, - ID: challenge, - RequestedAt: hr.RequestedAt, - HandledAt: sqlxx.NullTime(time.Now().UTC()), + Error: &p, + ConsentRequestID: cr.ConsentRequestID, + RequestedAt: hr.RequestedAt, + HandledAt: sqlxx.NullTime(time.Now().UTC()), }) if err != nil { h.r.Writer().WriteError(w, r, errorsx.WithStack(err)) diff --git a/consent/handler_test.go b/consent/handler_test.go index 4ee7ddfddff..273c6a48fe5 100644 --- a/consent/handler_test.go +++ b/consent/handler_test.go @@ -187,18 +187,18 @@ func TestGetConsentRequest(t *testing.T) { challenge, err = f.ToConsentChallenge(ctx, reg) require.NoError(t, err) require.NoError(t, reg.ConsentManager().CreateConsentRequest(ctx, f, &flow.OAuth2ConsentRequest{ - Client: cl, - ID: challenge, - Verifier: challenge, - CSRF: challenge, - LoginChallenge: sqlxx.NullString(lr.ID), + Client: cl, + ConsentRequestID: challenge, + Verifier: challenge, + CSRF: challenge, + LoginChallenge: sqlxx.NullString(lr.ID), })) if tc.handled { _, err := reg.ConsentManager().HandleConsentRequest(ctx, f, &flow.AcceptOAuth2ConsentRequest{ - ID: challenge, - WasHandled: true, - HandledAt: sqlxx.NullTime(time.Now()), + ConsentRequestID: challenge, + WasHandled: true, + HandledAt: sqlxx.NullTime(time.Now()), }) require.NoError(t, err) challenge, err = f.ToConsentChallenge(ctx, reg) diff --git a/consent/helper_test.go b/consent/helper_test.go index 4a347575f3a..de6e1319827 100644 --- a/consent/helper_test.go +++ b/consent/helper_test.go @@ -39,52 +39,52 @@ func TestSanitizeClient(t *testing.T) { func TestMatchScopes(t *testing.T) { for k, tc := range []struct { - granted []flow.AcceptOAuth2ConsentRequest - requested []string - expectChallenge string + granted []flow.AcceptOAuth2ConsentRequest + requested []string + expectedID string }{ { - granted: []flow.AcceptOAuth2ConsentRequest{{ID: "1", GrantedScope: []string{"foo", "bar"}}}, - requested: []string{"foo", "bar"}, - expectChallenge: "1", + granted: []flow.AcceptOAuth2ConsentRequest{{ConsentRequestID: "1", GrantedScope: []string{"foo", "bar"}}}, + requested: []string{"foo", "bar"}, + expectedID: "1", }, { - granted: []flow.AcceptOAuth2ConsentRequest{{ID: "1", GrantedScope: []string{"foo", "bar"}}}, - requested: []string{"foo", "bar", "baz"}, - expectChallenge: "", + granted: []flow.AcceptOAuth2ConsentRequest{{ConsentRequestID: "1", GrantedScope: []string{"foo", "bar"}}}, + requested: []string{"foo", "bar", "baz"}, + expectedID: "", }, { granted: []flow.AcceptOAuth2ConsentRequest{ - {ID: "1", GrantedScope: []string{"foo", "bar"}}, - {ID: "2", GrantedScope: []string{"foo", "bar"}}, + {ConsentRequestID: "1", GrantedScope: []string{"foo", "bar"}}, + {ConsentRequestID: "2", GrantedScope: []string{"foo", "bar"}}, }, - requested: []string{"foo", "bar"}, - expectChallenge: "1", + requested: []string{"foo", "bar"}, + expectedID: "1", }, { granted: []flow.AcceptOAuth2ConsentRequest{ - {ID: "1", GrantedScope: []string{"foo", "bar"}}, - {ID: "2", GrantedScope: []string{"foo", "bar", "baz"}}, + {ConsentRequestID: "1", GrantedScope: []string{"foo", "bar"}}, + {ConsentRequestID: "2", GrantedScope: []string{"foo", "bar", "baz"}}, }, - requested: []string{"foo", "bar", "baz"}, - expectChallenge: "2", + requested: []string{"foo", "bar", "baz"}, + expectedID: "2", }, { granted: []flow.AcceptOAuth2ConsentRequest{ - {ID: "1", GrantedScope: []string{"foo", "bar"}}, - {ID: "2", GrantedScope: []string{"foo", "bar", "baz"}}, + {ConsentRequestID: "1", GrantedScope: []string{"foo", "bar"}}, + {ConsentRequestID: "2", GrantedScope: []string{"foo", "bar", "baz"}}, }, - requested: []string{"zab"}, - expectChallenge: "", + requested: []string{"zab"}, + expectedID: "", }, } { t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) { got := matchScopes(fosite.ExactScopeStrategy, tc.granted, tc.requested) - if tc.expectChallenge == "" { + if tc.expectedID == "" { assert.Nil(t, got) return } - assert.Equal(t, tc.expectChallenge, got.ID) + assert.Equal(t, tc.expectedID, got.ConsentRequestID) }) } } diff --git a/consent/janitor_consent_test_helper.go b/consent/janitor_consent_test_helper.go index 645a88a2209..f40fa9f28de 100644 --- a/consent/janitor_consent_test_helper.go +++ b/consent/janitor_consent_test_helper.go @@ -32,7 +32,7 @@ func NewHandledLoginRequest(challenge string, hasError bool, requestedAt time.Ti } } -func NewHandledConsentRequest(challenge string, hasError bool, requestedAt time.Time, authenticatedAt sqlxx.NullTime) *flow.AcceptOAuth2ConsentRequest { +func NewHandledConsentRequest(consentRequestID string, hasError bool, requestedAt time.Time, authenticatedAt sqlxx.NullTime) *flow.AcceptOAuth2ConsentRequest { var deniedErr *flow.RequestDeniedError if hasError { deniedErr = &flow.RequestDeniedError{ @@ -46,11 +46,11 @@ func NewHandledConsentRequest(challenge string, hasError bool, requestedAt time. } return &flow.AcceptOAuth2ConsentRequest{ - ID: challenge, - HandledAt: sqlxx.NullTime(time.Now().Round(time.Second)), - Error: deniedErr, - RequestedAt: requestedAt, - AuthenticatedAt: authenticatedAt, - WasHandled: true, + ConsentRequestID: consentRequestID, + HandledAt: sqlxx.NullTime(time.Now().Round(time.Second)), + Error: deniedErr, + RequestedAt: requestedAt, + AuthenticatedAt: authenticatedAt, + WasHandled: true, } } diff --git a/consent/manager.go b/consent/manager.go index 3bd9152e07a..182064028ba 100644 --- a/consent/manager.go +++ b/consent/manager.go @@ -30,7 +30,7 @@ type ( HandleConsentRequest(ctx context.Context, f *flow.Flow, r *flow.AcceptOAuth2ConsentRequest) (*flow.OAuth2ConsentRequest, error) RevokeSubjectConsentSession(ctx context.Context, user string) error RevokeSubjectClientConsentSession(ctx context.Context, user, client string) error - RevokeConsentSessionByID(ctx context.Context, consentChallengeID string) error + RevokeConsentSessionByID(ctx context.Context, consentRequestID string) error VerifyAndInvalidateConsentRequest(ctx context.Context, verifier string) (*flow.AcceptOAuth2ConsentRequest, error) FindGrantedAndRememberedConsentRequests(ctx context.Context, client, user string) ([]flow.AcceptOAuth2ConsentRequest, error) diff --git a/consent/strategy_default.go b/consent/strategy_default.go index ef9342b09f2..9d091f32781 100644 --- a/consent/strategy_default.go +++ b/consent/strategy_default.go @@ -585,13 +585,13 @@ func (s *DefaultStrategy) forwardConsentRequest( // Set up csrf/challenge/verifier values verifier := strings.Replace(uuid.New(), "-", "", -1) - consentChallengeID := strings.Replace(uuid.New(), "-", "", -1) + consentRequestID := strings.Replace(uuid.New(), "-", "", -1) csrf := strings.Replace(uuid.New(), "-", "", -1) cl := sanitizeClientFromRequest(ar) consentRequest := &flow.OAuth2ConsentRequest{ - ID: consentChallengeID, + ConsentRequestID: consentRequestID, ACR: as.ACR, AMR: as.AMR, Verifier: verifier, diff --git a/consent/strategy_default_test.go b/consent/strategy_default_test.go index e1746d4bf0c..c4ba3d97107 100644 --- a/consent/strategy_default_test.go +++ b/consent/strategy_default_test.go @@ -47,7 +47,7 @@ func checkAndAcceptConsentHandler(t *testing.T, apiClient *hydra.APIClient, cb f payload := cb(t, res, err) v, _, err := apiClient.OAuth2API.AcceptOAuth2ConsentRequest(context.Background()). - ConsentChallenge(r.URL.Query().Get("consent_challenge")). + ConsentChallenge(res.Challenge). AcceptOAuth2ConsentRequest(payload). Execute() require.NoError(t, err) diff --git a/consent/strategy_oauth_test.go b/consent/strategy_oauth_test.go index e7d660f9fec..1c17d1bc814 100644 --- a/consent/strategy_oauth_test.go +++ b/consent/strategy_oauth_test.go @@ -207,11 +207,12 @@ func TestStrategyLoginConsentNext(t *testing.T) { testhelpers.NewLoginConsentUI(t, reg.Config(), func(w http.ResponseWriter, r *http.Request) { + loginChallenge = r.URL.Query().Get("login_challenge") res, _, err := adminClient.OAuth2API.GetOAuth2LoginRequest(ctx). - LoginChallenge(r.URL.Query().Get("login_challenge")). + LoginChallenge(loginChallenge). Execute() require.NoError(t, err) - loginChallenge = res.Challenge + require.Equal(t, loginChallenge, res.Challenge) v, _, err := adminClient.OAuth2API.AcceptOAuth2LoginRequest(ctx). LoginChallenge(loginChallenge). @@ -223,10 +224,11 @@ func TestStrategyLoginConsentNext(t *testing.T) { }, func(w http.ResponseWriter, r *http.Request) { consentChallenge = r.URL.Query().Get("consent_challenge") - _, _, err := adminClient.OAuth2API.GetOAuth2ConsentRequest(ctx). + res, _, err := adminClient.OAuth2API.GetOAuth2ConsentRequest(ctx). ConsentChallenge(consentChallenge). Execute() require.NoError(t, err) + require.Equal(t, consentChallenge, res.Challenge) v, _, err := adminClient.OAuth2API.AcceptOAuth2ConsentRequest(ctx). ConsentChallenge(consentChallenge). diff --git a/consent/test/manager_test_helpers.go b/consent/test/manager_test_helpers.go index a9239fd40cc..003f666f056 100644 --- a/consent/test/manager_test_helpers.go +++ b/consent/test/manager_test_helpers.go @@ -34,7 +34,7 @@ import ( func MockConsentRequest(key string, remember bool, rememberFor int, hasError bool, skip bool, authAt bool, loginChallengeBase string, network string) (c *flow.OAuth2ConsentRequest, h *flow.AcceptOAuth2ConsentRequest, f *flow.Flow) { c = &flow.OAuth2ConsentRequest{ - ID: makeID("challenge", network, key), + ConsentRequestID: makeID("challenge", network, key), RequestedScope: []string{"scopea" + key, "scopeb" + key}, RequestedAudience: []string{"auda" + key, "audb" + key}, Skip: skip, @@ -63,7 +63,7 @@ func MockConsentRequest(key string, remember bool, rememberFor int, hasError boo SessionID: c.LoginSessionID, Client: c.Client, State: flow.FlowStateConsentInitialized, - ConsentChallengeID: sqlxx.NullString(c.ID), + ConsentRequestID: sqlxx.NullString(c.ConsentRequestID), ConsentSkip: c.Skip, ConsentVerifier: sqlxx.NullString(c.Verifier), ConsentCSRF: sqlxx.NullString(c.CSRF), @@ -93,16 +93,16 @@ func MockConsentRequest(key string, remember bool, rememberFor int, hasError boo } h = &flow.AcceptOAuth2ConsentRequest{ - ConsentRequest: c, - RememberFor: rememberFor, - Remember: remember, - ID: makeID("challenge", network, key), - RequestedAt: time.Now().UTC().Add(-time.Minute), - AuthenticatedAt: authenticatedAt, - GrantedScope: []string{"scopea" + key, "scopeb" + key}, - GrantedAudience: []string{"auda" + key, "audb" + key}, - Error: err, - HandledAt: sqlxx.NullTime(time.Now().UTC()), + ConsentRequest: c, + RememberFor: rememberFor, + Remember: remember, + ConsentRequestID: makeID("challenge", network, key), + RequestedAt: time.Now().UTC().Add(-time.Minute), + AuthenticatedAt: authenticatedAt, + GrantedScope: []string{"scopea" + key, "scopeb" + key}, + GrantedAudience: []string{"auda" + key, "audb" + key}, + Error: err, + HandledAt: sqlxx.NullTime(time.Now().UTC()), // WasUsed: true, } @@ -196,17 +196,17 @@ func SaneMockHandleConsentRequest(t *testing.T, m consent.Manager, f *flow.Flow, } h := &flow.AcceptOAuth2ConsentRequest{ - ConsentRequest: c, - RememberFor: rememberFor, - Remember: remember, - ID: c.ID, - RequestedAt: time.Now().UTC().Add(-time.Minute), - AuthenticatedAt: sqlxx.NullTime(authAt), - GrantedScope: []string{"scopea", "scopeb"}, - GrantedAudience: []string{"auda", "audb"}, - Error: rde, - WasHandled: false, - HandledAt: sqlxx.NullTime(time.Now().UTC().Add(-time.Minute)), + ConsentRequest: c, + RememberFor: rememberFor, + Remember: remember, + ConsentRequestID: c.ConsentRequestID, + RequestedAt: time.Now().UTC().Add(-time.Minute), + AuthenticatedAt: sqlxx.NullTime(authAt), + GrantedScope: []string{"scopea", "scopeb"}, + GrantedAudience: []string{"auda", "audb"}, + Error: rde, + WasHandled: false, + HandledAt: sqlxx.NullTime(time.Now().UTC().Add(-time.Minute)), } _, err := m.HandleConsentRequest(context.Background(), f, h) @@ -237,9 +237,9 @@ func SaneMockConsentRequest(t *testing.T, m consent.Manager, f *flow.Flow, skip RequestedAt: time.Now().UTC().Add(-time.Hour), Context: sqlxx.JSONRawMessage(`{"foo": "bar"}`), - ID: uuid.New().String(), - Verifier: uuid.New().String(), - CSRF: uuid.New().String(), + ConsentRequestID: uuid.New().String(), + Verifier: uuid.New().String(), + CSRF: uuid.New().String(), } require.NoError(t, m.CreateConsentRequest(context.Background(), f, c)) @@ -535,7 +535,6 @@ func ManagerTests(deps Deps, m consent.Manager, clientManager client.Manager, fo require.Error(t, err) consentChallenge = x.Must(f.ToConsentChallenge(ctx, deps)) - // consentRequest.ID = consentChallenge err = m.CreateConsentRequest(ctx, f, consentRequest) require.NoError(t, err) @@ -558,9 +557,8 @@ func ManagerTests(deps Deps, m consent.Manager, clientManager client.Manager, fo got2, err := m.VerifyAndInvalidateConsentRequest(ctx, consentVerifier) require.NoError(t, err) - consentRequest.ID = got2.ID compareConsentRequest(t, consentRequest, got2.ConsentRequest) - assert.Equal(t, consentRequest.ID, got2.ID) + assert.Equal(t, consentRequest.ConsentRequestID, got2.ConsentRequestID) assert.Equal(t, h.GrantedAudience, got2.GrantedAudience) t.Run("sub=detect double-submit for consent verifier", func(t *testing.T) { @@ -678,24 +676,24 @@ func ManagerTests(deps Deps, m consent.Manager, clientManager client.Manager, fo require.NoError(t, fositeManager.CreateAccessTokenSession( ctx, makeID("", network, "trva1"), - &fosite.Request{Client: cr1.Client, ID: crr1.ID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, + &fosite.Request{Client: cr1.Client, ID: crr1.ConsentRequestID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, )) require.NoError(t, fositeManager.CreateRefreshTokenSession( ctx, makeID("", network, "rrva1"), "", - &fosite.Request{Client: cr1.Client, ID: crr1.ID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, + &fosite.Request{Client: cr1.Client, ID: crr1.ConsentRequestID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, )) require.NoError(t, fositeManager.CreateAccessTokenSession( ctx, makeID("", network, "trva2"), - &fosite.Request{Client: cr2.Client, ID: crr2.ID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, + &fosite.Request{Client: cr2.Client, ID: crr2.ConsentRequestID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, )) require.NoError(t, fositeManager.CreateRefreshTokenSession( ctx, makeID("", network, "rrva2"), "", - &fosite.Request{Client: cr2.Client, ID: crr2.ID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, + &fosite.Request{Client: cr2.Client, ID: crr2.ConsentRequestID, RequestedAt: time.Now(), Session: &oauth2.Session{DefaultSession: openid.NewDefaultSession()}}, )) for i, tc := range []struct { @@ -779,87 +777,87 @@ func ManagerTests(deps Deps, m consent.Manager, clientManager client.Manager, fo for i, tc := range []struct { subject string sid string - challenges []string + consentIDs []string clients []string }{ { subject: cr1.Subject, sid: makeID("fk-login-session", network, "rv1"), - challenges: []string{handledConsentRequest1.ID}, + consentIDs: []string{handledConsentRequest1.ConsentRequestID}, clients: []string{"fk-client-rv1"}, }, { subject: cr2.Subject, sid: makeID("fk-login-session", network, "rv2"), - challenges: []string{handledConsentRequest2.ID}, + consentIDs: []string{handledConsentRequest2.ConsentRequestID}, clients: []string{"fk-client-rv2"}, }, { subject: "subjectrv3", sid: makeID("fk-login-session", network, "rv2"), - challenges: []string{}, + consentIDs: []string{}, clients: []string{}, }, } { t.Run(fmt.Sprintf("case=%d/subject=%s/session=%s", i, tc.subject, tc.sid), func(t *testing.T) { consents, err := m.FindSubjectsSessionGrantedConsentRequests(ctx, tc.subject, tc.sid, 100, 0) - assert.Equal(t, len(tc.challenges), len(consents)) + assert.Equal(t, len(tc.consentIDs), len(consents)) - if len(tc.challenges) == 0 { + if len(tc.consentIDs) == 0 { assert.EqualError(t, err, consent.ErrNoPreviousConsentFound.Error()) } else { require.NoError(t, err) for _, consent := range consents { - assert.Contains(t, tc.challenges, consent.ID) + assert.Contains(t, tc.consentIDs, consent.ConsentRequestID) assert.Contains(t, tc.clients, consent.ConsentRequest.Client.GetID()) } } n, err := m.CountSubjectsGrantedConsentRequests(ctx, tc.subject) require.NoError(t, err) - assert.Equal(t, n, len(tc.challenges)) + assert.Equal(t, n, len(tc.consentIDs)) }) } for i, tc := range []struct { - subject string - challenges []string - clients []string + subject string + consentRequestIDs []string + clients []string }{ { - subject: "subjectrv1", - challenges: []string{handledConsentRequest1.ID}, - clients: []string{"fk-client-rv1"}, + subject: "subjectrv1", + consentRequestIDs: []string{handledConsentRequest1.ConsentRequestID}, + clients: []string{"fk-client-rv1"}, }, { - subject: "subjectrv2", - challenges: []string{handledConsentRequest2.ID}, - clients: []string{"fk-client-rv2"}, + subject: "subjectrv2", + consentRequestIDs: []string{handledConsentRequest2.ConsentRequestID}, + clients: []string{"fk-client-rv2"}, }, { - subject: "subjectrv3", - challenges: []string{}, - clients: []string{}, + subject: "subjectrv3", + consentRequestIDs: []string{}, + clients: []string{}, }, } { t.Run(fmt.Sprintf("case=%d/subject=%s", i, tc.subject), func(t *testing.T) { consents, err := m.FindSubjectsGrantedConsentRequests(ctx, tc.subject, 100, 0) - assert.Equal(t, len(tc.challenges), len(consents)) + assert.Equal(t, len(tc.consentRequestIDs), len(consents)) - if len(tc.challenges) == 0 { + if len(tc.consentRequestIDs) == 0 { assert.EqualError(t, err, consent.ErrNoPreviousConsentFound.Error()) } else { require.NoError(t, err) for _, consent := range consents { - assert.Contains(t, tc.challenges, consent.ID) + assert.Contains(t, tc.consentRequestIDs, consent.ConsentRequestID) assert.Contains(t, tc.clients, consent.ConsentRequest.Client.GetID()) } } n, err := m.CountSubjectsGrantedConsentRequests(ctx, tc.subject) require.NoError(t, err) - assert.Equal(t, n, len(tc.challenges)) + assert.Equal(t, n, len(tc.consentRequestIDs)) }) } @@ -1076,7 +1074,7 @@ func ManagerTests(deps Deps, m consent.Manager, clientManager client.Manager, fo f, err := m.CreateLoginRequest(ctx, lr) require.NoError(t, err) expected := &flow.OAuth2ConsentRequest{ - ID: uuid.NewString(), + ConsentRequestID: uuid.NewString(), Skip: true, Subject: subject, OpenIDConnectContext: nil, @@ -1096,14 +1094,14 @@ func ManagerTests(deps Deps, m consent.Manager, clientManager client.Manager, fo result, err := m.GetConsentRequest(ctx, consentChallenge) require.NoError(t, err) - assert.EqualValues(t, expected.ID, result.ID) + assert.EqualValues(t, expected.ConsentRequestID, result.ConsentRequestID) _, err = m.DeleteLoginSession(ctx, s.ID) require.NoError(t, err) result, err = m.GetConsentRequest(ctx, consentChallenge) require.NoError(t, err) - assert.EqualValues(t, expected.ID, result.ID) + assert.EqualValues(t, expected.ConsentRequestID, result.ConsentRequestID) }) } } @@ -1137,7 +1135,7 @@ func compareAuthenticationRequest(t *testing.T, a, b *flow.LoginRequest) { func compareConsentRequest(t *testing.T, a, b *flow.OAuth2ConsentRequest) { assert.EqualValues(t, a.Client.GetID(), b.Client.GetID()) - assert.EqualValues(t, a.ID, b.ID) + assert.EqualValues(t, a.ConsentRequestID, b.ConsentRequestID) assert.EqualValues(t, *a.OpenIDConnectContext, *b.OpenIDConnectContext) assert.EqualValues(t, a.Subject, b.Subject) assert.EqualValues(t, a.RequestedScope, b.RequestedScope) diff --git a/flow/.snapshots/TestOAuth2ConsentRequest_MarshalJSON.json b/flow/.snapshots/TestOAuth2ConsentRequest_MarshalJSON.json index 1a39fb2e6c2..ec893803186 100644 --- a/flow/.snapshots/TestOAuth2ConsentRequest_MarshalJSON.json +++ b/flow/.snapshots/TestOAuth2ConsentRequest_MarshalJSON.json @@ -1 +1 @@ -"{\"challenge\":\"\",\"requested_scope\":[],\"requested_access_token_audience\":[],\"skip\":false,\"subject\":\"\",\"oidc_context\":null,\"client\":null,\"request_url\":\"\",\"login_challenge\":\"\",\"login_session_id\":\"\",\"acr\":\"\",\"amr\":[]}" +"{\"challenge\":\"\",\"consent_request_id\":\"\",\"requested_scope\":[],\"requested_access_token_audience\":[],\"skip\":false,\"subject\":\"\",\"oidc_context\":null,\"client\":null,\"request_url\":\"\",\"login_challenge\":\"\",\"login_session_id\":\"\",\"acr\":\"\",\"amr\":[]}" diff --git a/flow/.snapshots/TestOAuth2ConsentSession_MarshalJSON.json b/flow/.snapshots/TestOAuth2ConsentSession_MarshalJSON.json index c08b431eac6..1320cf058ad 100644 --- a/flow/.snapshots/TestOAuth2ConsentSession_MarshalJSON.json +++ b/flow/.snapshots/TestOAuth2ConsentSession_MarshalJSON.json @@ -1 +1 @@ -"{\"grant_scope\":[],\"grant_access_token_audience\":[],\"session\":null,\"remember\":false,\"remember_for\":0,\"handled_at\":null,\"context\":{},\"consent_request\":null}" +"{\"consent_request_id\":\"\",\"grant_scope\":[],\"grant_access_token_audience\":[],\"session\":null,\"remember\":false,\"remember_for\":0,\"handled_at\":null,\"context\":{},\"consent_request\":null}" diff --git a/flow/consent_types.go b/flow/consent_types.go index 936dbf2b8e5..1216760118f 100644 --- a/flow/consent_types.go +++ b/flow/consent_types.go @@ -145,8 +145,8 @@ func (e *RequestDeniedError) Value() (driver.Value, error) { // // swagger:model acceptOAuth2ConsentRequest type AcceptOAuth2ConsentRequest struct { - // ID instead of Challenge because of pop - ID string `json:"-"` + // ConsentRequestID is for internal use only. + ConsentRequestID string `json:"-"` // GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`. GrantedScope sqlxx.StringSliceJSONFormat `json:"grant_scope"` @@ -214,9 +214,7 @@ func (r *AcceptOAuth2ConsentRequest) HasError() bool { // List of OAuth 2.0 Consent Sessions // // swagger:model oAuth2ConsentSessions -// -//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions -type oAuth2ConsentSessions []OAuth2ConsentSession +type _ []OAuth2ConsentSession // OAuth 2.0 Consent Session // @@ -224,45 +222,46 @@ type oAuth2ConsentSessions []OAuth2ConsentSession // // swagger:model oAuth2ConsentSession type OAuth2ConsentSession struct { - ID string `json:"-" db:"challenge"` + // ConsentRequestID is the identifier of the consent request that initiated this consent session. + ConsentRequestID string `json:"consent_request_id"` // Scope Granted // // GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`. - GrantedScope sqlxx.StringSliceJSONFormat `json:"grant_scope" db:"granted_scope"` + GrantedScope sqlxx.StringSliceJSONFormat `json:"grant_scope"` // Audience Granted // // GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`. - GrantedAudience sqlxx.StringSliceJSONFormat `json:"grant_access_token_audience" db:"granted_at_audience"` + GrantedAudience sqlxx.StringSliceJSONFormat `json:"grant_access_token_audience"` // Session Details // // Session allows you to set (optional) session data for access and ID tokens. - Session *AcceptOAuth2ConsentRequestSession `json:"session" db:"-"` + Session *AcceptOAuth2ConsentRequestSession `json:"session"` // Remember Consent // // Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same // client asks the same user for the same, or a subset of, scope. - Remember bool `json:"remember" db:"remember"` + Remember bool `json:"remember"` // Remember Consent For // // RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the // authorization will be remembered indefinitely. - RememberFor int `json:"remember_for" db:"remember_for"` + RememberFor int `json:"remember_for"` // Consent Handled At // // HandledAt contains the timestamp the consent request was handled. - HandledAt sqlxx.NullTime `json:"handled_at" db:"handled_at"` + HandledAt sqlxx.NullTime `json:"handled_at"` // If set to true means that the request was already handled. This // can happen on form double-submit or other errors. If this is set // we recommend redirecting the user to `request_url` to re-initiate // the flow. - WasHandled bool `json:"-" db:"was_used"` + WasHandled bool `json:"-"` // Context is an optional object which can hold arbitrary data. The data will be made available when fetching the // consent request under the "context" field. This is useful in scenarios where login and consent endpoints share @@ -272,14 +271,14 @@ type OAuth2ConsentSession struct { // Consent Request // // The consent request that lead to this consent session. - ConsentRequest *OAuth2ConsentRequest `json:"consent_request" db:"-"` + ConsentRequest *OAuth2ConsentRequest `json:"consent_request"` - Error *RequestDeniedError `json:"-" db:"error"` - RequestedAt time.Time `json:"-" db:"requested_at"` - AuthenticatedAt sqlxx.NullTime `json:"-" db:"authenticated_at"` + Error *RequestDeniedError `json:"-"` + RequestedAt time.Time `json:"-"` + AuthenticatedAt sqlxx.NullTime `json:"-"` - SessionIDToken sqlxx.MapStringInterface `db:"session_id_token" json:"-"` - SessionAccessToken sqlxx.MapStringInterface `db:"session_access_token" json:"-"` + SessionIDToken sqlxx.MapStringInterface `json:"-"` + SessionAccessToken sqlxx.MapStringInterface `json:"-"` } func (r *OAuth2ConsentSession) MarshalJSON() ([]byte, error) { @@ -305,7 +304,6 @@ func (r *OAuth2ConsentSession) MarshalJSON() ([]byte, error) { // // swagger:model acceptOAuth2LoginRequest type HandledLoginRequest struct { - // ID instead of challenge for pop ID string `json:"-"` // Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store @@ -627,10 +625,13 @@ func (r *LoginRequest) MarshalJSON() ([]byte, error) { // // swagger:model oAuth2ConsentRequest type OAuth2ConsentRequest struct { - // ID is the identifier of the consent authorization request. + // Challenge is used to retrieve/accept/deny the consent request. // // required: true - ID string `json:"challenge"` + Challenge string `json:"challenge"` + + // ConsentRequestID is the ID of the consent request. + ConsentRequestID string `json:"consent_request_id"` // RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client. RequestedScope sqlxx.StringSliceJSONFormat `json:"requested_scope"` diff --git a/flow/flow.go b/flow/flow.go index 24d086bd895..b44e14322bb 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -82,11 +82,14 @@ const ( type Flow struct { // ID is the identifier of the login request. // - // The struct field is named ID for compatibility with gobuffalo/pop. + // The struct field is named ID for compatibility with gobuffalo/pop, and is + // the primary key in the database. // // The database column should be named `login_challenge_id`, but is not for // historical reasons. - ID string `db:"login_challenge" json:"i"` // PK in database + // + // This is not the same as the login session ID. + ID string `db:"login_challenge" json:"i"` NID uuid.UUID `db:"nid" json:"n"` // RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client. @@ -204,8 +207,9 @@ type Flow struct { LoginError *RequestDeniedError `db:"login_error" json:"le,omitempty"` LoginAuthenticatedAt sqlxx.NullTime `db:"login_authenticated_at" json:"la,omitempty"` - // ConsentChallengeID is the identifier of the consent request. - ConsentChallengeID sqlxx.NullString `db:"consent_challenge_id" json:"cc,omitempty"` + // ConsentRequestID is the identifier of the consent request. + // The database column should be named `consent_request_id`, but is not for historical reasons. + ConsentRequestID sqlxx.NullString `db:"consent_challenge_id" json:"cc,omitempty"` // ConsentSkip, if true, implies that the client has requested the same scopes from the same user previously. // If true, you must not ask the user to grant the requested scopes. You must however either allow or deny the @@ -377,8 +381,8 @@ func (f *Flow) HandleConsentRequest(r *AcceptOAuth2ConsentRequest) error { return errors.Errorf("invalid flow state: expected %d/%d/%d, got %d", FlowStateConsentInitialized, FlowStateConsentUnused, FlowStateConsentError, f.State) } - if f.ConsentChallengeID.String() != r.ID { - return errors.Errorf("flow.ConsentChallengeID %s doesn't match AcceptOAuth2ConsentRequest.ID %s", f.ConsentChallengeID.String(), r.ID) + if f.ConsentRequestID.String() != r.ConsentRequestID { + return errors.Errorf("flow.ConsentRequestID %s doesn't match AcceptOAuth2ConsentRequest.ID %s", f.ConsentRequestID.String(), r.ConsentRequestID) } if r.Error != nil { @@ -420,9 +424,10 @@ func (f *Flow) InvalidateConsentRequest() error { return nil } -func (f *Flow) GetConsentRequest() *OAuth2ConsentRequest { +func (f *Flow) GetConsentRequest(challenge string) *OAuth2ConsentRequest { cs := OAuth2ConsentRequest{ - ID: f.ConsentChallengeID.String(), + Challenge: challenge, + ConsentRequestID: f.ConsentRequestID.String(), RequestedScope: f.RequestedScope, RequestedAudience: f.RequestedAudience, Skip: f.ConsentSkip, @@ -455,7 +460,7 @@ func (f *Flow) GetHandledConsentRequest() *AcceptOAuth2ConsentRequest { crf = *f.ConsentRememberFor } return &AcceptOAuth2ConsentRequest{ - ID: f.ConsentChallengeID.String(), + ConsentRequestID: f.ConsentRequestID.String(), GrantedScope: f.GrantedScope, GrantedAudience: f.GrantedAudience, Session: &AcceptOAuth2ConsentRequestSession{AccessToken: f.SessionAccessToken, IDToken: f.SessionIDToken}, @@ -464,7 +469,7 @@ func (f *Flow) GetHandledConsentRequest() *AcceptOAuth2ConsentRequest { HandledAt: f.ConsentHandledAt, WasHandled: f.ConsentWasHandled, Context: f.Context, - ConsentRequest: f.GetConsentRequest(), + ConsentRequest: f.GetConsentRequest( /* No longer available and no longer needed: challenge = */ ""), Error: f.ConsentError, RequestedAt: f.RequestedAt, AuthenticatedAt: f.LoginAuthenticatedAt, @@ -509,7 +514,7 @@ type CipherProvider interface { } // ToLoginChallenge converts the flow into a login challenge. -func (f Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) { +func (f Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvider) (challenge string, err error) { if f.Client != nil { f.ClientID = f.Client.GetID() } @@ -517,7 +522,7 @@ func (f Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvide } // ToLoginVerifier converts the flow into a login verifier. -func (f Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) { +func (f Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider) (verifier string, err error) { if f.Client != nil { f.ClientID = f.Client.GetID() } @@ -525,7 +530,7 @@ func (f Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider } // ToConsentChallenge converts the flow into a consent challenge. -func (f Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) { +func (f Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvider) (challenge string, err error) { if f.Client != nil { f.ClientID = f.Client.GetID() } @@ -533,7 +538,7 @@ func (f Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvi } // ToConsentVerifier converts the flow into a consent verifier. -func (f Flow) ToConsentVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) { +func (f Flow) ToConsentVerifier(ctx context.Context, cipherProvider CipherProvider) (verifier string, err error) { if f.Client != nil { f.ClientID = f.Client.GetID() } diff --git a/flow/flow_test.go b/flow/flow_test.go index 43a54176088..1b56336b0af 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -52,7 +52,7 @@ func (f *Flow) setHandledLoginRequest(r *HandledLoginRequest) { } func (f *Flow) setConsentRequest(r OAuth2ConsentRequest) { - f.ConsentChallengeID = sqlxx.NullString(r.ID) + f.ConsentRequestID = sqlxx.NullString(r.ConsentRequestID) f.RequestedScope = r.RequestedScope f.RequestedAudience = r.RequestedAudience f.ConsentSkip = r.Skip @@ -75,7 +75,7 @@ func (f *Flow) setConsentRequest(r OAuth2ConsentRequest) { } func (f *Flow) setHandledConsentRequest(r AcceptOAuth2ConsentRequest) { - f.ConsentChallengeID = sqlxx.NullString(r.ID) + f.ConsentRequestID = sqlxx.NullString(r.ConsentRequestID) f.GrantedScope = r.GrantedScope f.GrantedAudience = r.GrantedAudience f.ConsentRemember = r.Remember @@ -191,7 +191,7 @@ func TestFlow_GetConsentRequest(t *testing.T) { expected := OAuth2ConsentRequest{} assert.NoError(t, faker.FakeData(&expected)) f.setConsentRequest(expected) - actual := f.GetConsentRequest() + actual := f.GetConsentRequest(expected.Challenge) assert.Equal(t, expected, *actual) }) } @@ -203,7 +203,7 @@ func TestFlow_HandleConsentRequest(t *testing.T) { expected := AcceptOAuth2ConsentRequest{} require.NoError(t, faker.FakeData(&expected)) - expected.ID = string(f.ConsentChallengeID) + expected.ConsentRequestID = string(f.ConsentRequestID) expected.HandledAt = sqlxx.NullTime(time.Now()) expected.RequestedAt = f.RequestedAt expected.Session = &AcceptOAuth2ConsentRequestSession{ diff --git a/go.mod b/go.mod index 06714a3dc32..2a9413c0ea5 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/ory/hydra-client-go/v2 v2.2.1 github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/kratos-client-go v1.2.1 - github.com/ory/x v0.0.675 + github.com/ory/x v0.0.695 github.com/pborman/uuid v1.2.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.1 diff --git a/go.sum b/go.sum index bad2adc7cee..f621e7822b7 100644 --- a/go.sum +++ b/go.sum @@ -394,8 +394,8 @@ github.com/ory/kratos-client-go v1.2.1 h1:Q3T/adfAfAkHFcV1LGLnwz4QkY6ghBdX9zde5T github.com/ory/kratos-client-go v1.2.1/go.mod h1:WiQYlrqW4Atj6Js7oDN5ArbZxo0nTO2u/e1XaDv2yMI= github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b h1:BIzoOe2/wynZBQak1po0tzgvARseIKsR2bF6b+SZoKE= github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= -github.com/ory/x v0.0.675 h1:K6GpVo99BXBFv2UiwMjySNNNqCFKGswynrt7vWQJFU8= -github.com/ory/x v0.0.675/go.mod h1:zJmnDtKje2FCP4EeFvRsKk94XXiqKCSGJMZcirAfhUs= +github.com/ory/x v0.0.695 h1:cUOQPtKO0MChwwXBB0sycrT/c5MU3la8fz3I45tR/VU= +github.com/ory/x v0.0.695/go.mod h1:CLnDxtKBkO2MoOYRbQe9HASUlqTBl7AP9r7YcmyD1CY= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= diff --git a/internal/httpclient/api/openapi.yaml b/internal/httpclient/api/openapi.yaml index ff6a133454f..7c0e3493c09 100644 --- a/internal/httpclient/api/openapi.yaml +++ b/internal/httpclient/api/openapi.yaml @@ -1048,12 +1048,12 @@ paths: type: string style: form - description: |- - Consent Challenge ID + Consent Request ID If set, revoke all token chains derived from this particular consent request ID. explode: true in: query - name: consent_challenge_id + name: consent_request_id required: false schema: type: string @@ -3032,6 +3032,7 @@ components: type: object oAuth2ConsentRequest: example: + consent_request_id: consent_request_id requested_access_token_audience: - requested_access_token_audience - requested_access_token_audience @@ -3138,10 +3139,13 @@ components: \ JSON for SQL storage." type: array challenge: - description: ID is the identifier of the consent authorization request. + description: Challenge is used to retrieve/accept/deny the consent request. type: string client: $ref: '#/components/schemas/oAuth2Client' + consent_request_id: + description: ConsentRequestID is the ID of the consent request. + type: string context: title: "JSONRawMessage represents a json.RawMessage that works well with\ \ JSON, SQL, and Swagger." @@ -3258,8 +3262,10 @@ components: oAuth2ConsentSession: description: A completed OAuth 2.0 Consent Session. example: + consent_request_id: consent_request_id remember: true consent_request: + consent_request_id: consent_request_id requested_access_token_audience: - requested_access_token_audience - requested_access_token_audience @@ -3374,6 +3380,10 @@ components: properties: consent_request: $ref: '#/components/schemas/oAuth2ConsentRequest' + consent_request_id: + description: ConsentRequestID is the identifier of the consent request that + initiated this consent session. + type: string context: title: "JSONRawMessage represents a json.RawMessage that works well with\ \ JSON, SQL, and Swagger." diff --git a/internal/httpclient/api_o_auth2.go b/internal/httpclient/api_o_auth2.go index a4d29f867a4..867f4a2af49 100644 --- a/internal/httpclient/api_o_auth2.go +++ b/internal/httpclient/api_o_auth2.go @@ -2881,12 +2881,12 @@ func (a *OAuth2APIService) RejectOAuth2LogoutRequestExecute(r ApiRejectOAuth2Log } type ApiRevokeOAuth2ConsentSessionsRequest struct { - ctx context.Context - ApiService *OAuth2APIService - subject *string - client *string - consentChallengeId *string - all *bool + ctx context.Context + ApiService *OAuth2APIService + subject *string + client *string + consentRequestId *string + all *bool } // OAuth 2.0 Consent Subject The subject whose consent sessions should be deleted. @@ -2901,9 +2901,9 @@ func (r ApiRevokeOAuth2ConsentSessionsRequest) Client(client string) ApiRevokeOA return r } -// Consent Challenge ID If set, revoke all token chains derived from this particular consent request ID. -func (r ApiRevokeOAuth2ConsentSessionsRequest) ConsentChallengeId(consentChallengeId string) ApiRevokeOAuth2ConsentSessionsRequest { - r.consentChallengeId = &consentChallengeId +// Consent Request ID If set, revoke all token chains derived from this particular consent request ID. +func (r ApiRevokeOAuth2ConsentSessionsRequest) ConsentRequestId(consentRequestId string) ApiRevokeOAuth2ConsentSessionsRequest { + r.consentRequestId = &consentRequestId return r } @@ -2958,8 +2958,8 @@ func (a *OAuth2APIService) RevokeOAuth2ConsentSessionsExecute(r ApiRevokeOAuth2C if r.client != nil { parameterAddToHeaderOrQuery(localVarQueryParams, "client", r.client, "") } - if r.consentChallengeId != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "consent_challenge_id", r.consentChallengeId, "") + if r.consentRequestId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "consent_request_id", r.consentRequestId, "") } if r.all != nil { parameterAddToHeaderOrQuery(localVarQueryParams, "all", r.all, "") diff --git a/internal/httpclient/docs/OAuth2API.md b/internal/httpclient/docs/OAuth2API.md index d9fca64a803..a9a8f672ccb 100644 --- a/internal/httpclient/docs/OAuth2API.md +++ b/internal/httpclient/docs/OAuth2API.md @@ -1532,7 +1532,7 @@ No authorization required ## RevokeOAuth2ConsentSessions -> RevokeOAuth2ConsentSessions(ctx).Subject(subject).Client(client).ConsentChallengeId(consentChallengeId).All(all).Execute() +> RevokeOAuth2ConsentSessions(ctx).Subject(subject).Client(client).ConsentRequestId(consentRequestId).All(all).Execute() Revoke OAuth 2.0 Consent Sessions of a Subject @@ -1553,12 +1553,12 @@ import ( func main() { subject := "subject_example" // string | OAuth 2.0 Consent Subject The subject whose consent sessions should be deleted. (optional) client := "client_example" // string | OAuth 2.0 Client ID If set, deletes only those consent sessions that have been granted to the specified OAuth 2.0 Client ID. (optional) - consentChallengeId := "consentChallengeId_example" // string | Consent Challenge ID If set, revoke all token chains derived from this particular consent request ID. (optional) + consentRequestId := "consentRequestId_example" // string | Consent Request ID If set, revoke all token chains derived from this particular consent request ID. (optional) all := true // bool | Revoke All Consent Sessions If set to `true` deletes all consent sessions by the Subject that have been granted. (optional) configuration := openapiclient.NewConfiguration() apiClient := openapiclient.NewAPIClient(configuration) - r, err := apiClient.OAuth2API.RevokeOAuth2ConsentSessions(context.Background()).Subject(subject).Client(client).ConsentChallengeId(consentChallengeId).All(all).Execute() + r, err := apiClient.OAuth2API.RevokeOAuth2ConsentSessions(context.Background()).Subject(subject).Client(client).ConsentRequestId(consentRequestId).All(all).Execute() if err != nil { fmt.Fprintf(os.Stderr, "Error when calling `OAuth2API.RevokeOAuth2ConsentSessions``: %v\n", err) fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) @@ -1579,7 +1579,7 @@ Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **subject** | **string** | OAuth 2.0 Consent Subject The subject whose consent sessions should be deleted. | **client** | **string** | OAuth 2.0 Client ID If set, deletes only those consent sessions that have been granted to the specified OAuth 2.0 Client ID. | - **consentChallengeId** | **string** | Consent Challenge ID If set, revoke all token chains derived from this particular consent request ID. | + **consentRequestId** | **string** | Consent Request ID If set, revoke all token chains derived from this particular consent request ID. | **all** | **bool** | Revoke All Consent Sessions If set to `true` deletes all consent sessions by the Subject that have been granted. | ### Return type diff --git a/internal/httpclient/docs/OAuth2ConsentRequest.md b/internal/httpclient/docs/OAuth2ConsentRequest.md index ae2be51002d..1197410e106 100644 --- a/internal/httpclient/docs/OAuth2ConsentRequest.md +++ b/internal/httpclient/docs/OAuth2ConsentRequest.md @@ -6,8 +6,9 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **Acr** | Pointer to **string** | ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. | [optional] **Amr** | Pointer to **[]string** | | [optional] -**Challenge** | **string** | ID is the identifier of the consent authorization request. | +**Challenge** | **string** | Challenge is used to retrieve/accept/deny the consent request. | **Client** | Pointer to [**OAuth2Client**](OAuth2Client.md) | | [optional] +**ConsentRequestId** | Pointer to **string** | ConsentRequestID is the ID of the consent request. | [optional] **Context** | Pointer to **interface{}** | | [optional] **LoginChallenge** | Pointer to **string** | LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate a login and consent request in the login & consent app. | [optional] **LoginSessionId** | Pointer to **string** | LoginSessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user. | [optional] @@ -132,6 +133,31 @@ SetClient sets Client field to given value. HasClient returns a boolean if a field has been set. +### GetConsentRequestId + +`func (o *OAuth2ConsentRequest) GetConsentRequestId() string` + +GetConsentRequestId returns the ConsentRequestId field if non-nil, zero value otherwise. + +### GetConsentRequestIdOk + +`func (o *OAuth2ConsentRequest) GetConsentRequestIdOk() (*string, bool)` + +GetConsentRequestIdOk returns a tuple with the ConsentRequestId field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetConsentRequestId + +`func (o *OAuth2ConsentRequest) SetConsentRequestId(v string)` + +SetConsentRequestId sets ConsentRequestId field to given value. + +### HasConsentRequestId + +`func (o *OAuth2ConsentRequest) HasConsentRequestId() bool` + +HasConsentRequestId returns a boolean if a field has been set. + ### GetContext `func (o *OAuth2ConsentRequest) GetContext() interface{}` diff --git a/internal/httpclient/docs/OAuth2ConsentSession.md b/internal/httpclient/docs/OAuth2ConsentSession.md index 0399f2ab121..9537990f6bd 100644 --- a/internal/httpclient/docs/OAuth2ConsentSession.md +++ b/internal/httpclient/docs/OAuth2ConsentSession.md @@ -5,6 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **ConsentRequest** | Pointer to [**OAuth2ConsentRequest**](OAuth2ConsentRequest.md) | | [optional] +**ConsentRequestId** | Pointer to **string** | ConsentRequestID is the identifier of the consent request that initiated this consent session. | [optional] **Context** | Pointer to **interface{}** | | [optional] **ExpiresAt** | Pointer to [**OAuth2ConsentSessionExpiresAt**](OAuth2ConsentSessionExpiresAt.md) | | [optional] **GrantAccessTokenAudience** | Pointer to **[]string** | | [optional] @@ -58,6 +59,31 @@ SetConsentRequest sets ConsentRequest field to given value. HasConsentRequest returns a boolean if a field has been set. +### GetConsentRequestId + +`func (o *OAuth2ConsentSession) GetConsentRequestId() string` + +GetConsentRequestId returns the ConsentRequestId field if non-nil, zero value otherwise. + +### GetConsentRequestIdOk + +`func (o *OAuth2ConsentSession) GetConsentRequestIdOk() (*string, bool)` + +GetConsentRequestIdOk returns a tuple with the ConsentRequestId field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetConsentRequestId + +`func (o *OAuth2ConsentSession) SetConsentRequestId(v string)` + +SetConsentRequestId sets ConsentRequestId field to given value. + +### HasConsentRequestId + +`func (o *OAuth2ConsentSession) HasConsentRequestId() bool` + +HasConsentRequestId returns a boolean if a field has been set. + ### GetContext `func (o *OAuth2ConsentSession) GetContext() interface{}` diff --git a/internal/httpclient/model_o_auth2_consent_request.go b/internal/httpclient/model_o_auth2_consent_request.go index ef753515dbb..552c5c1e50f 100644 --- a/internal/httpclient/model_o_auth2_consent_request.go +++ b/internal/httpclient/model_o_auth2_consent_request.go @@ -25,10 +25,12 @@ type OAuth2ConsentRequest struct { // ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. Acr *string `json:"acr,omitempty"` Amr []string `json:"amr,omitempty"` - // ID is the identifier of the consent authorization request. + // Challenge is used to retrieve/accept/deny the consent request. Challenge string `json:"challenge"` Client *OAuth2Client `json:"client,omitempty"` - Context interface{} `json:"context,omitempty"` + // ConsentRequestID is the ID of the consent request. + ConsentRequestId *string `json:"consent_request_id,omitempty"` + Context interface{} `json:"context,omitempty"` // LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate a login and consent request in the login & consent app. LoginChallenge *string `json:"login_challenge,omitempty"` // LoginSessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the \"sid\" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user. @@ -184,6 +186,38 @@ func (o *OAuth2ConsentRequest) SetClient(v OAuth2Client) { o.Client = &v } +// GetConsentRequestId returns the ConsentRequestId field value if set, zero value otherwise. +func (o *OAuth2ConsentRequest) GetConsentRequestId() string { + if o == nil || IsNil(o.ConsentRequestId) { + var ret string + return ret + } + return *o.ConsentRequestId +} + +// GetConsentRequestIdOk returns a tuple with the ConsentRequestId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *OAuth2ConsentRequest) GetConsentRequestIdOk() (*string, bool) { + if o == nil || IsNil(o.ConsentRequestId) { + return nil, false + } + return o.ConsentRequestId, true +} + +// HasConsentRequestId returns a boolean if a field has been set. +func (o *OAuth2ConsentRequest) HasConsentRequestId() bool { + if o != nil && !IsNil(o.ConsentRequestId) { + return true + } + + return false +} + +// SetConsentRequestId gets a reference to the given string and assigns it to the ConsentRequestId field. +func (o *OAuth2ConsentRequest) SetConsentRequestId(v string) { + o.ConsentRequestId = &v +} + // GetContext returns the Context field value if set, zero value otherwise (both if not set or set to explicit null). func (o *OAuth2ConsentRequest) GetContext() interface{} { if o == nil { @@ -493,6 +527,9 @@ func (o OAuth2ConsentRequest) ToMap() (map[string]interface{}, error) { if !IsNil(o.Client) { toSerialize["client"] = o.Client } + if !IsNil(o.ConsentRequestId) { + toSerialize["consent_request_id"] = o.ConsentRequestId + } if o.Context != nil { toSerialize["context"] = o.Context } diff --git a/internal/httpclient/model_o_auth2_consent_session.go b/internal/httpclient/model_o_auth2_consent_session.go index 92772ab891f..1f7057e696f 100644 --- a/internal/httpclient/model_o_auth2_consent_session.go +++ b/internal/httpclient/model_o_auth2_consent_session.go @@ -21,7 +21,9 @@ var _ MappedNullable = &OAuth2ConsentSession{} // OAuth2ConsentSession A completed OAuth 2.0 Consent Session. type OAuth2ConsentSession struct { - ConsentRequest *OAuth2ConsentRequest `json:"consent_request,omitempty"` + ConsentRequest *OAuth2ConsentRequest `json:"consent_request,omitempty"` + // ConsentRequestID is the identifier of the consent request that initiated this consent session. + ConsentRequestId *string `json:"consent_request_id,omitempty"` Context interface{} `json:"context,omitempty"` ExpiresAt *OAuth2ConsentSessionExpiresAt `json:"expires_at,omitempty"` GrantAccessTokenAudience []string `json:"grant_access_token_audience,omitempty"` @@ -83,6 +85,38 @@ func (o *OAuth2ConsentSession) SetConsentRequest(v OAuth2ConsentRequest) { o.ConsentRequest = &v } +// GetConsentRequestId returns the ConsentRequestId field value if set, zero value otherwise. +func (o *OAuth2ConsentSession) GetConsentRequestId() string { + if o == nil || IsNil(o.ConsentRequestId) { + var ret string + return ret + } + return *o.ConsentRequestId +} + +// GetConsentRequestIdOk returns a tuple with the ConsentRequestId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *OAuth2ConsentSession) GetConsentRequestIdOk() (*string, bool) { + if o == nil || IsNil(o.ConsentRequestId) { + return nil, false + } + return o.ConsentRequestId, true +} + +// HasConsentRequestId returns a boolean if a field has been set. +func (o *OAuth2ConsentSession) HasConsentRequestId() bool { + if o != nil && !IsNil(o.ConsentRequestId) { + return true + } + + return false +} + +// SetConsentRequestId gets a reference to the given string and assigns it to the ConsentRequestId field. +func (o *OAuth2ConsentSession) SetConsentRequestId(v string) { + o.ConsentRequestId = &v +} + // GetContext returns the Context field value if set, zero value otherwise (both if not set or set to explicit null). func (o *OAuth2ConsentSession) GetContext() interface{} { if o == nil { @@ -353,6 +387,9 @@ func (o OAuth2ConsentSession) ToMap() (map[string]interface{}, error) { if !IsNil(o.ConsentRequest) { toSerialize["consent_request"] = o.ConsentRequest } + if !IsNil(o.ConsentRequestId) { + toSerialize["consent_request_id"] = o.ConsentRequestId + } if o.Context != nil { toSerialize["context"] = o.Context } diff --git a/internal/testhelpers/janitor_test_helper.go b/internal/testhelpers/janitor_test_helper.go index c452b3248f1..89007a12585 100644 --- a/internal/testhelpers/janitor_test_helper.go +++ b/internal/testhelpers/janitor_test_helper.go @@ -302,15 +302,15 @@ func (j *JanitorConsentTestHelper) ConsentRejectionSetup(ctx context.Context, re f.LoginAuthenticatedAt = consentRequest.AuthenticatedAt // Reject the consents - if consentRequest.ID == j.flushConsentRequests[0].ID { + if consentRequest.ConsentRequestID == j.flushConsentRequests[0].ConsentRequestID { // accept this one _, err = cm.HandleConsentRequest(ctx, f, consent.NewHandledConsentRequest( - consentRequest.ID, false, consentRequest.RequestedAt, consentRequest.AuthenticatedAt)) + consentRequest.ConsentRequestID, false, consentRequest.RequestedAt, consentRequest.AuthenticatedAt)) require.NoError(t, err) continue } _, err = cm.HandleConsentRequest(ctx, f, consent.NewHandledConsentRequest( - consentRequest.ID, true, consentRequest.RequestedAt, consentRequest.AuthenticatedAt)) + consentRequest.ConsentRequestID, true, consentRequest.RequestedAt, consentRequest.AuthenticatedAt)) require.NoError(t, err) } } @@ -320,8 +320,7 @@ func (j *JanitorConsentTestHelper) ConsentRejectionValidate(ctx context.Context, return func(t *testing.T) { var err error for _, r := range j.flushConsentRequests { - t.Logf("check consent: %s", r.ID) - _, err = cm.GetConsentRequest(ctx, r.ID) + _, err = cm.GetConsentRequest(ctx, r.Challenge) // Consent requests should never be persisted. require.Error(t, err) } @@ -406,11 +405,11 @@ func (j *JanitorConsentTestHelper) ConsentTimeoutSetup(ctx context.Context, reg if i == 0 { // Create at least 1 consent request that has been accepted _, err = cm.HandleConsentRequest(ctx, f, &flow.AcceptOAuth2ConsentRequest{ - ID: consentRequest.ID, - WasHandled: true, - HandledAt: sqlxx.NullTime(time.Now()), - RequestedAt: consentRequest.RequestedAt, - AuthenticatedAt: consentRequest.AuthenticatedAt, + ConsentRequestID: consentRequest.ConsentRequestID, + WasHandled: true, + HandledAt: sqlxx.NullTime(time.Now()), + RequestedAt: consentRequest.RequestedAt, + AuthenticatedAt: consentRequest.AuthenticatedAt, }) require.NoError(t, err) } @@ -424,7 +423,7 @@ func (j *JanitorConsentTestHelper) ConsentTimeoutValidate(ctx context.Context, c var err error for _, r := range j.flushConsentRequests { - _, err = cm.GetConsentRequest(ctx, r.ID) + _, err = cm.GetConsentRequest(ctx, r.Challenge) require.Error(t, err, "Unverified consent requests are never pesisted") } } @@ -809,7 +808,7 @@ func genLoginRequests(uniqueName string, lifespan time.Duration) []*flow.LoginRe func genConsentRequests(uniqueName string, lifespan time.Duration) []*flow.OAuth2ConsentRequest { return []*flow.OAuth2ConsentRequest{ { - ID: fmt.Sprintf("%s_flush-consent-1", uniqueName), + ConsentRequestID: fmt.Sprintf("%s_flush-consent-1", uniqueName), RequestedScope: []string{"foo", "bar"}, Subject: fmt.Sprintf("%s_flush-consent-1", uniqueName), OpenIDConnectContext: nil, @@ -821,7 +820,7 @@ func genConsentRequests(uniqueName string, lifespan time.Duration) []*flow.OAuth CSRF: fmt.Sprintf("%s_flush-consent-1", uniqueName), }, { - ID: fmt.Sprintf("%s_flush-consent-2", uniqueName), + ConsentRequestID: fmt.Sprintf("%s_flush-consent-2", uniqueName), RequestedScope: []string{"foo", "bar"}, Subject: fmt.Sprintf("%s_flush-consent-2", uniqueName), OpenIDConnectContext: nil, @@ -833,7 +832,7 @@ func genConsentRequests(uniqueName string, lifespan time.Duration) []*flow.OAuth CSRF: fmt.Sprintf("%s_flush-consent-2", uniqueName), }, { - ID: fmt.Sprintf("%s_flush-consent-3", uniqueName), + ConsentRequestID: fmt.Sprintf("%s_flush-consent-3", uniqueName), RequestedScope: []string{"foo", "bar"}, Subject: fmt.Sprintf("%s_flush-consent-3", uniqueName), OpenIDConnectContext: nil, diff --git a/oauth2/fosite_store_helpers_test.go b/oauth2/fosite_store_helpers_test.go index 1084e31629c..d8eeffbd95f 100644 --- a/oauth2/fosite_store_helpers_test.go +++ b/oauth2/fosite_store_helpers_test.go @@ -113,7 +113,7 @@ func mockRequestForeignKey(t *testing.T, id string, x oauth2.InternalRegistry) { Client: cl, OpenIDConnectContext: new(flow.OAuth2ConsentRequestOpenIDConnectContext), LoginChallenge: sqlxx.NullString(id), - ID: id, + ConsentRequestID: id, Verifier: id, CSRF: id, AuthenticatedAt: sqlxx.NullTime(time.Now()), @@ -138,16 +138,16 @@ func mockRequestForeignKey(t *testing.T, id string, x oauth2.InternalRegistry) { err = x.ConsentManager().CreateConsentRequest(ctx, f, cr) require.NoError(t, err) - encodedFlow, err := f.ToConsentVerifier(ctx, x) + _, err = f.ToConsentVerifier(ctx, x) require.NoError(t, err) _, err = x.ConsentManager().HandleConsentRequest(ctx, f, &flow.AcceptOAuth2ConsentRequest{ - ConsentRequest: cr, - Session: new(flow.AcceptOAuth2ConsentRequestSession), - AuthenticatedAt: sqlxx.NullTime(time.Now()), - ID: encodedFlow, - RequestedAt: time.Now(), - HandledAt: sqlxx.NullTime(time.Now()), + ConsentRequest: cr, + Session: new(flow.AcceptOAuth2ConsentRequestSession), + AuthenticatedAt: sqlxx.NullTime(time.Now()), + ConsentRequestID: cr.ConsentRequestID, + RequestedAt: time.Now(), + HandledAt: sqlxx.NullTime(time.Now()), }) require.NoError(t, err) diff --git a/oauth2/handler.go b/oauth2/handler.go index 3f1a633038d..61c6ae4ddd1 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -1125,7 +1125,7 @@ func (h *Handler) oAuth2Authorize(w http.ResponseWriter, r *http.Request, _ http return } - authorizeRequest.SetID(session.ID) + authorizeRequest.SetID(session.ConsentRequestID) claims := &jwt.IDTokenClaims{ Subject: obfuscatedSubject, Issuer: h.c.IssuerURL(ctx).String(), @@ -1160,7 +1160,7 @@ func (h *Handler) oAuth2Authorize(w http.ResponseWriter, r *http.Request, _ http Extra: session.Session.AccessToken, KID: accessTokenKeyID, ClientID: authorizeRequest.GetClient().GetID(), - ConsentChallenge: session.ID, + ConsentChallenge: session.ConsentRequestID, ExcludeNotBeforeClaim: h.c.ExcludeNotBeforeClaim(ctx), AllowedTopLevelClaims: h.c.AllowedTopLevelClaims(ctx), MirrorTopLevelClaims: h.c.MirrorTopLevelClaims(ctx), diff --git a/oauth2/oauth2_auth_code_test.go b/oauth2/oauth2_auth_code_test.go index cd17509855c..fdd4ba94121 100644 --- a/oauth2/oauth2_auth_code_test.go +++ b/oauth2/oauth2_auth_code_test.go @@ -119,8 +119,10 @@ func acceptLoginHandler(t *testing.T, c *client.Client, adminClient *hydra.APICl func acceptConsentHandler(t *testing.T, c *client.Client, adminClient *hydra.APIClient, reg driver.Registry, subject string, checkRequestPayload func(*hydra.OAuth2ConsentRequest) *hydra.AcceptOAuth2ConsentRequest) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - rr, _, err := adminClient.OAuth2API.GetOAuth2ConsentRequest(context.Background()).ConsentChallenge(r.URL.Query().Get("consent_challenge")).Execute() + challenge := r.URL.Query().Get("consent_challenge") + rr, _, err := adminClient.OAuth2API.GetOAuth2ConsentRequest(context.Background()).ConsentChallenge(challenge).Execute() require.NoError(t, err) + require.Equal(t, challenge, rr.Challenge) assert.EqualValues(t, c.GetID(), pointerx.Deref(rr.Client.ClientId)) assert.Empty(t, pointerx.Deref(rr.Client.ClientSecret)) @@ -149,7 +151,7 @@ func acceptConsentHandler(t *testing.T, c *client.Client, adminClient *hydra.API } v, _, err := adminClient.OAuth2API.AcceptOAuth2ConsentRequest(context.Background()). - ConsentChallenge(r.URL.Query().Get("consent_challenge")). + ConsentChallenge(challenge). AcceptOAuth2ConsentRequest(acceptBody). Execute() require.NoError(t, err) @@ -1212,15 +1214,18 @@ func TestAuthCodeWithDefaultStrategy(t *testing.T) { acceptLoginHandler(t, c, adminClient, reg, subject, nil), acceptConsentHandler(t, c, adminClient, reg, subject, func(ocr *hydra.OAuth2ConsentRequest) *hydra.AcceptOAuth2ConsentRequest { require.NotZero(t, ocr.Challenge) - t.Logf("Consent Request ID: %s", ocr.Challenge) + require.NotNil(t, ocr.ConsentRequestId) + require.NotZero(t, *ocr.ConsentRequestId) + t.Logf("Consent challenge: %s", ocr.Challenge) + t.Logf("Consent request ID: %s", *ocr.ConsentRequestId) return &hydra.AcceptOAuth2ConsentRequest{ GrantScope: ocr.RequestedScope, GrantAccessTokenAudience: ocr.RequestedAccessTokenAudience, Remember: pointerx.Ptr(true), RememberFor: pointerx.Ptr[int64](0), Session: &hydra.AcceptOAuth2ConsentRequestSession{ - AccessToken: map[string]interface{}{"crid": ocr.Challenge}, - IdToken: map[string]interface{}{"crid": ocr.Challenge}, + AccessToken: map[string]interface{}{"crid": ocr.ConsentRequestId}, + IdToken: map[string]interface{}{"crid": ocr.ConsentRequestId}, }, } }), @@ -1260,7 +1265,7 @@ func TestAuthCodeWithDefaultStrategy(t *testing.T) { // revoken the first token chain by consent request id _, err = adminClient.OAuth2API. RevokeOAuth2ConsentSessions(context.Background()). - ConsentChallengeId(consentRequestID). + ConsentRequestId(consentRequestID). Execute() require.NoError(t, err) diff --git a/persistence/sql/persister_consent.go b/persistence/sql/persister_consent.go index 168e099afe6..7b314f275f0 100644 --- a/persistence/sql/persister_consent.go +++ b/persistence/sql/persister_consent.go @@ -44,12 +44,12 @@ func (p *Persister) RevokeSubjectClientConsentSession(ctx context.Context, user, return p.Transaction(ctx, p.revokeConsentSession("consent_challenge_id IS NOT NULL AND subject = ? AND client_id = ?", user, client)) } -func (p *Persister) RevokeConsentSessionByID(ctx context.Context, consentChallengeID string) (err error) { +func (p *Persister) RevokeConsentSessionByID(ctx context.Context, consentRequestID string) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.RevokeConsentSessionByID", - trace.WithAttributes(attribute.String("consent_challenge_id", consentChallengeID))) + trace.WithAttributes(attribute.String("consent_challenge_id", consentRequestID))) defer otelx.End(span, &err) - return p.Transaction(ctx, p.revokeConsentSession("consent_challenge_id = ?", consentChallengeID)) + return p.Transaction(ctx, p.revokeConsentSession("consent_challenge_id = ?", consentRequestID)) } func (p *Persister) revokeConsentSession(whereStmt string, whereArgs ...interface{}) func(context.Context, *pop.Connection) error { @@ -67,7 +67,7 @@ func (p *Persister) revokeConsentSession(whereStmt string, whereArgs ...interfac ids := make([]interface{}, 0, len(fs)) nid := p.NetworkID(ctx) for _, f := range fs { - ids = append(ids, f.ConsentChallengeID.String()) + ids = append(ids, f.ConsentRequestID.String()) } if len(ids) == 0 { @@ -183,7 +183,7 @@ func (p *Persister) CreateConsentRequest(ctx context.Context, f *flow.Flow, req return errorsx.WithStack(x.ErrNotFound) } f.State = flow.FlowStateConsentInitialized - f.ConsentChallengeID = sqlxx.NullString(req.ID) + f.ConsentRequestID = sqlxx.NullString(req.ConsentRequestID) f.ConsentSkip = req.Skip f.ConsentVerifier = sqlxx.NullString(req.Verifier) f.ConsentCSRF = sqlxx.NullString(req.CSRF) @@ -195,7 +195,6 @@ func (p *Persister) GetFlowByConsentChallenge(ctx context.Context, challenge str ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetFlowByConsentChallenge") defer otelx.End(span, &err) - // challenge contains the flow. f, err := flowctx.Decode[flow.Flow](ctx, p.r.FlowCipher(), challenge, flowctx.AsConsentChallenge) if err != nil { return nil, errorsx.WithStack(x.ErrNotFound) @@ -222,7 +221,7 @@ func (p *Persister) GetConsentRequest(ctx context.Context, challenge string) (_ return nil, err } - return f.GetConsentRequest(), nil + return f.GetConsentRequest(challenge), nil } func (p *Persister) CreateLoginRequest(ctx context.Context, req *flow.LoginRequest) (_ *flow.Flow, err error) { @@ -285,14 +284,11 @@ func (p *Persister) HandleConsentRequest(ctx context.Context, f *flow.Flow, r *f if f.NID != p.NetworkID(ctx) { return nil, errorsx.WithStack(x.ErrNotFound) } - // Restore the short challenge ID, which was previously sent to the encoded flow, - // to make sure that the challenge ID in the returned flow matches the param. - r.ID = f.ConsentChallengeID.String() if err := f.HandleConsentRequest(r); err != nil { return nil, errorsx.WithStack(err) } - return f.GetConsentRequest(), nil + return f.GetConsentRequest( /* No longer available and no longer needed: challenge = */ ""), nil } func (p *Persister) VerifyAndInvalidateConsentRequest(ctx context.Context, verifier string) (_ *flow.AcceptOAuth2ConsentRequest, err error) { diff --git a/persistence/sql/persister_nid_test.go b/persistence/sql/persister_nid_test.go index 93bccdcfe58..99090a47690 100644 --- a/persistence/sql/persister_nid_test.go +++ b/persistence/sql/persister_nid_test.go @@ -380,11 +380,11 @@ func (s *PersisterTestSuite) TestCreateConsentRequest() { require.NoError(t, r.Persister().Connection(context.Background()).Create(f)) req := &flow.OAuth2ConsentRequest{ - ID: "consent-request-id", - LoginChallenge: sqlxx.NullString(f.ID), - Skip: false, - Verifier: "verifier", - CSRF: "csrf", + ConsentRequestID: "consent-request-id", + LoginChallenge: sqlxx.NullString(f.ID), + Skip: false, + Verifier: "verifier", + CSRF: "csrf", } require.NoError(t, r.Persister().CreateConsentRequest(s.t1, f, req)) @@ -769,17 +769,17 @@ func (s *PersisterTestSuite) TestFindGrantedAndRememberedConsentRequests() { require.NoError(t, r.Persister().CreateClient(s.t1, client)) req := &flow.OAuth2ConsentRequest{ - ID: "consent-request-id", - LoginChallenge: sqlxx.NullString(f.ID), - Skip: false, - Verifier: "verifier", - CSRF: "csrf", + ConsentRequestID: "consent-request-id", + LoginChallenge: sqlxx.NullString(f.ID), + Skip: false, + Verifier: "verifier", + CSRF: "csrf", } hcr := &flow.AcceptOAuth2ConsentRequest{ - ID: req.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: req.ConsentRequestID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, } require.NoError(t, r.Persister().CreateConsentRequest(s.t1, f, req)) _, err := r.Persister().HandleConsentRequest(s.t1, f, hcr) @@ -809,17 +809,17 @@ func (s *PersisterTestSuite) TestFindSubjectsGrantedConsentRequests() { require.NoError(t, r.Persister().Connection(context.Background()).Create(f)) req := &flow.OAuth2ConsentRequest{ - ID: "consent-request-id", - LoginChallenge: sqlxx.NullString(f.ID), - Skip: false, - Verifier: "verifier", - CSRF: "csrf", + ConsentRequestID: "consent-request-id", + LoginChallenge: sqlxx.NullString(f.ID), + Skip: false, + Verifier: "verifier", + CSRF: "csrf", } hcr := &flow.AcceptOAuth2ConsentRequest{ - ID: req.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: req.ConsentRequestID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, } require.NoError(t, r.Persister().CreateConsentRequest(s.t1, f, req)) _, err := r.Persister().HandleConsentRequest(s.t1, f, hcr) @@ -1096,19 +1096,20 @@ func (s *PersisterTestSuite) TestGetConsentRequest() { require.NoError(t, r.Persister().Connection(context.Background()).Create(f)) req := &flow.OAuth2ConsentRequest{ - ID: x.Must(f.ToConsentChallenge(s.t1, r)), - LoginChallenge: sqlxx.NullString(f.ID), - Skip: false, - Verifier: "verifier", - CSRF: "csrf", + Challenge: x.Must(f.ToConsentChallenge(s.t1, r)), + ConsentRequestID: f.GetConsentRequest("").ConsentRequestID, + LoginChallenge: sqlxx.NullString(f.ID), + Skip: false, + Verifier: "verifier", + CSRF: "csrf", } require.NoError(t, r.Persister().CreateConsentRequest(s.t1, f, req)) - actual, err := r.Persister().GetConsentRequest(s.t2, req.ID) + actual, err := r.Persister().GetConsentRequest(s.t2, req.Challenge) require.Error(t, err) require.Nil(t, actual) - actual, err = r.Persister().GetConsentRequest(s.t1, req.ID) + actual, err = r.Persister().GetConsentRequest(s.t1, req.Challenge) require.NoError(t, err) require.NotNil(t, actual) }) @@ -1436,17 +1437,17 @@ func (s *PersisterTestSuite) TestHandleConsentRequest() { require.NoError(t, r.Persister().CreateClient(s.t2, c1)) req := &flow.OAuth2ConsentRequest{ - ID: "consent-request-id", - LoginChallenge: sqlxx.NullString(f.ID), - Skip: false, - Verifier: "verifier", - CSRF: "csrf", + ConsentRequestID: "consent-request-id", + LoginChallenge: sqlxx.NullString(f.ID), + Skip: false, + Verifier: "verifier", + CSRF: "csrf", } hcr := &flow.AcceptOAuth2ConsentRequest{ - ID: req.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: req.ConsentRequestID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, } require.NoError(t, r.Persister().CreateConsentRequest(s.t1, f, req)) @@ -1523,17 +1524,17 @@ func (s *PersisterTestSuite) TestListUserAuthenticatedClientsWithBackChannelLogo require.NoError(t, r.Persister().CreateClient(s.t2, c2)) t1f1 := newFlow(s.t1NID, c1.ID, "sub", sqlxx.NullString(uuid.Must(uuid.NewV4()).String())) - t1f1.ConsentChallengeID = "t1f1-consent-challenge" + t1f1.ConsentRequestID = "t1f1-consent-challenge" t1f1.LoginVerifier = "t1f1-login-verifier" t1f1.ConsentVerifier = "t1f1-consent-verifier" t2f1 := newFlow(s.t2NID, c1.ID, "sub", t1f1.SessionID) - t2f1.ConsentChallengeID = "t2f1-consent-challenge" + t2f1.ConsentRequestID = "t2f1-consent-challenge" t2f1.LoginVerifier = "t2f1-login-verifier" t2f1.ConsentVerifier = "t2f1-consent-verifier" t2f2 := newFlow(s.t2NID, c2.ID, "sub", t1f1.SessionID) - t2f2.ConsentChallengeID = "t2f2-consent-challenge" + t2f2.ConsentRequestID = "t2f2-consent-challenge" t2f2.LoginVerifier = "t2f2-login-verifier" t2f2.ConsentVerifier = "t2f2-consent-verifier" @@ -1544,43 +1545,43 @@ func (s *PersisterTestSuite) TestListUserAuthenticatedClientsWithBackChannelLogo require.NoError(t, r.Persister().Connection(context.Background()).Create(t2f2)) require.NoError(t, r.Persister().CreateConsentRequest(s.t1, t1f1, &flow.OAuth2ConsentRequest{ - ID: t1f1.ID, - LoginChallenge: sqlxx.NullString(t1f1.ID), - Skip: false, - Verifier: t1f1.ConsentVerifier.String(), - CSRF: "csrf", + ConsentRequestID: t1f1.ID, + LoginChallenge: sqlxx.NullString(t1f1.ID), + Skip: false, + Verifier: t1f1.ConsentVerifier.String(), + CSRF: "csrf", })) require.NoError(t, r.Persister().CreateConsentRequest(s.t2, t2f1, &flow.OAuth2ConsentRequest{ - ID: t2f1.ID, - LoginChallenge: sqlxx.NullString(t2f1.ID), - Skip: false, - Verifier: t2f1.ConsentVerifier.String(), - CSRF: "csrf", + ConsentRequestID: t2f1.ID, + LoginChallenge: sqlxx.NullString(t2f1.ID), + Skip: false, + Verifier: t2f1.ConsentVerifier.String(), + CSRF: "csrf", })) require.NoError(t, r.Persister().CreateConsentRequest(s.t2, t2f2, &flow.OAuth2ConsentRequest{ - ID: t2f2.ID, - LoginChallenge: sqlxx.NullString(t2f2.ID), - Skip: false, - Verifier: t2f2.ConsentVerifier.String(), - CSRF: "csrf", + ConsentRequestID: t2f2.ID, + LoginChallenge: sqlxx.NullString(t2f2.ID), + Skip: false, + Verifier: t2f2.ConsentVerifier.String(), + CSRF: "csrf", })) _, err := r.Persister().HandleConsentRequest(s.t1, t1f1, &flow.AcceptOAuth2ConsentRequest{ - ID: t1f1.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: t1f1.ID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, }) require.NoError(t, err) _, err = r.Persister().HandleConsentRequest(s.t2, t2f1, &flow.AcceptOAuth2ConsentRequest{ - ID: t2f1.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: t2f1.ID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, }) require.NoError(t, err) _, err = r.Persister().HandleConsentRequest(s.t2, t2f2, &flow.AcceptOAuth2ConsentRequest{ - ID: t2f2.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: t2f2.ID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, }) require.NoError(t, err) @@ -1606,17 +1607,17 @@ func (s *PersisterTestSuite) TestListUserAuthenticatedClientsWithFrontChannelLog require.NoError(t, r.Persister().CreateClient(s.t2, c2)) t1f1 := newFlow(s.t1NID, c1.ID, "sub", sqlxx.NullString(uuid.Must(uuid.NewV4()).String())) - t1f1.ConsentChallengeID = "t1f1-consent-challenge" + t1f1.ConsentRequestID = "t1f1-consent-challenge" t1f1.LoginVerifier = "t1f1-login-verifier" t1f1.ConsentVerifier = "t1f1-consent-verifier" t2f1 := newFlow(s.t2NID, c1.ID, "sub", t1f1.SessionID) - t2f1.ConsentChallengeID = "t2f1-consent-challenge" + t2f1.ConsentRequestID = "t2f1-consent-challenge" t2f1.LoginVerifier = "t2f1-login-verifier" t2f1.ConsentVerifier = "t2f1-consent-verifier" t2f2 := newFlow(s.t2NID, c2.ID, "sub", t1f1.SessionID) - t2f2.ConsentChallengeID = "t2f2-consent-challenge" + t2f2.ConsentRequestID = "t2f2-consent-challenge" t2f2.LoginVerifier = "t2f2-login-verifier" t2f2.ConsentVerifier = "t2f2-consent-verifier" @@ -1627,43 +1628,43 @@ func (s *PersisterTestSuite) TestListUserAuthenticatedClientsWithFrontChannelLog require.NoError(t, r.Persister().Connection(context.Background()).Create(t2f2)) require.NoError(t, r.Persister().CreateConsentRequest(s.t1, t1f1, &flow.OAuth2ConsentRequest{ - ID: t1f1.ID, - LoginChallenge: sqlxx.NullString(t1f1.ID), - Skip: false, - Verifier: t1f1.ConsentVerifier.String(), - CSRF: "csrf", + ConsentRequestID: t1f1.ID, + LoginChallenge: sqlxx.NullString(t1f1.ID), + Skip: false, + Verifier: t1f1.ConsentVerifier.String(), + CSRF: "csrf", })) require.NoError(t, r.Persister().CreateConsentRequest(s.t2, t2f1, &flow.OAuth2ConsentRequest{ - ID: t2f1.ID, - LoginChallenge: sqlxx.NullString(t2f1.ID), - Skip: false, - Verifier: t2f1.ConsentVerifier.String(), - CSRF: "csrf", + ConsentRequestID: t2f1.ID, + LoginChallenge: sqlxx.NullString(t2f1.ID), + Skip: false, + Verifier: t2f1.ConsentVerifier.String(), + CSRF: "csrf", })) require.NoError(t, r.Persister().CreateConsentRequest(s.t2, t2f2, &flow.OAuth2ConsentRequest{ - ID: t2f2.ID, - LoginChallenge: sqlxx.NullString(t2f2.ID), - Skip: false, - Verifier: t2f2.ConsentVerifier.String(), - CSRF: "csrf", + ConsentRequestID: t2f2.ID, + LoginChallenge: sqlxx.NullString(t2f2.ID), + Skip: false, + Verifier: t2f2.ConsentVerifier.String(), + CSRF: "csrf", })) _, err := r.Persister().HandleConsentRequest(s.t1, t1f1, &flow.AcceptOAuth2ConsentRequest{ - ID: t1f1.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: t1f1.ID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, }) require.NoError(t, err) _, err = r.Persister().HandleConsentRequest(s.t2, t2f1, &flow.AcceptOAuth2ConsentRequest{ - ID: t2f1.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: t2f1.ID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, }) require.NoError(t, err) _, err = r.Persister().HandleConsentRequest(s.t2, t2f2, &flow.AcceptOAuth2ConsentRequest{ - ID: t2f2.ID, - HandledAt: sqlxx.NullTime(time.Now()), - Remember: true, + ConsentRequestID: t2f2.ID, + HandledAt: sqlxx.NullTime(time.Now()), + Remember: true, }) require.NoError(t, err) @@ -2225,20 +2226,20 @@ func newClient() *client.Client { func newFlow(nid uuid.UUID, clientID string, subject string, sessionID sqlxx.NullString) *flow.Flow { return &flow.Flow{ - NID: nid, - ID: uuid.Must(uuid.NewV4()).String(), - ClientID: clientID, - Subject: subject, - ConsentError: &flow.RequestDeniedError{}, - State: flow.FlowStateConsentUnused, - LoginError: &flow.RequestDeniedError{}, - Context: sqlxx.JSONRawMessage{}, - AMR: sqlxx.StringSliceJSONFormat{}, - ConsentChallengeID: sqlxx.NullString("not-null"), - ConsentVerifier: sqlxx.NullString("not-null"), - ConsentCSRF: sqlxx.NullString("not-null"), - SessionID: sessionID, - RequestedAt: time.Now(), + NID: nid, + ID: uuid.Must(uuid.NewV4()).String(), + ClientID: clientID, + Subject: subject, + ConsentError: &flow.RequestDeniedError{}, + State: flow.FlowStateConsentUnused, + LoginError: &flow.RequestDeniedError{}, + Context: sqlxx.JSONRawMessage{}, + AMR: sqlxx.StringSliceJSONFormat{}, + ConsentRequestID: sqlxx.NullString("not-null"), + ConsentVerifier: sqlxx.NullString("not-null"), + ConsentCSRF: sqlxx.NullString("not-null"), + SessionID: sessionID, + RequestedAt: time.Now(), } } diff --git a/spec/api.json b/spec/api.json index a251a3aab07..840e6d7d827 100644 --- a/spec/api.json +++ b/spec/api.json @@ -839,12 +839,16 @@ "$ref": "#/components/schemas/StringSliceJSONFormat" }, "challenge": { - "description": "ID is the identifier of the consent authorization request.", + "description": "Challenge is used to retrieve/accept/deny the consent request.", "type": "string" }, "client": { "$ref": "#/components/schemas/oAuth2Client" }, + "consent_request_id": { + "description": "ConsentRequestID is the ID of the consent request.", + "type": "string" + }, "context": { "$ref": "#/components/schemas/JSONRawMessage" }, @@ -923,6 +927,10 @@ "consent_request": { "$ref": "#/components/schemas/oAuth2ConsentRequest" }, + "consent_request_id": { + "description": "ConsentRequestID is the identifier of the consent request that initiated this consent session.", + "type": "string" + }, "context": { "$ref": "#/components/schemas/JSONRawMessage" }, @@ -2908,9 +2916,9 @@ } }, { - "description": "Consent Challenge ID\n\nIf set, revoke all token chains derived from this particular consent request ID.", + "description": "Consent Request ID\n\nIf set, revoke all token chains derived from this particular consent request ID.", "in": "query", - "name": "consent_challenge_id", + "name": "consent_request_id", "schema": { "type": "string" } diff --git a/spec/swagger.json b/spec/swagger.json index 0a0b48dad58..21fd7799148 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -1259,8 +1259,8 @@ }, { "type": "string", - "description": "Consent Challenge ID\n\nIf set, revoke all token chains derived from this particular consent request ID.", - "name": "consent_challenge_id", + "description": "Consent Request ID\n\nIf set, revoke all token chains derived from this particular consent request ID.", + "name": "consent_request_id", "in": "query" }, { @@ -2867,12 +2867,16 @@ "$ref": "#/definitions/StringSliceJSONFormat" }, "challenge": { - "description": "ID is the identifier of the consent authorization request.", + "description": "Challenge is used to retrieve/accept/deny the consent request.", "type": "string" }, "client": { "$ref": "#/definitions/oAuth2Client" }, + "consent_request_id": { + "description": "ConsentRequestID is the ID of the consent request.", + "type": "string" + }, "context": { "$ref": "#/definitions/JSONRawMessage" }, @@ -2948,6 +2952,10 @@ "consent_request": { "$ref": "#/definitions/oAuth2ConsentRequest" }, + "consent_request_id": { + "description": "ConsentRequestID is the identifier of the consent request that initiated this consent session.", + "type": "string" + }, "context": { "$ref": "#/definitions/JSONRawMessage" }, diff --git a/x/events/events.go b/x/events/events.go index 998892f77d9..afc8eb4ae81 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -109,6 +109,11 @@ func WithSubject(subject string) trace.EventOption { return trace.WithAttributes(otelattr.String(attributeKeyOAuth2Subject, subject)) } +// WithSubject emits the consent request ID as part of the event. +func WithConsentRequestID(id string) trace.EventOption { + return trace.WithAttributes(ConsentRequestID(id)) +} + // WithRequest emits the subject and client ID from the fosite request as part of the event. func WithRequest(request fosite.Requester) trace.EventOption { var attributes []otelattr.KeyValue