From c004fee69497a5a0f8af5ccb6a2ab8d104fd9249 Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Mon, 14 Aug 2023 12:39:45 +0200 Subject: [PATCH] feat: propagate logout to identity provider (#3596) * feat: propagate logout to identity provider This commit improves the integration between Hydra and Kratos when logging out the user. This adds a new configuration key for configuring a Kratos admin URL. Additionally, Kratos can send a session ID when accepting a login request. If a session ID was specified and a Kratos admin URL was configured, Hydra will disable the corresponding Kratos session through the admin API if a frontchannel or backchannel logout was triggered. * fix: add special case for MySQL * chore: update sdk * chore: consistent naming * fix: cleanup persister --- consent/manager.go | 2 +- consent/manager_test_helpers.go | 14 ++- consent/registry.go | 2 + consent/strategy_default.go | 25 +++-- consent/strategy_default_test.go | 14 +-- consent/strategy_logout_test.go | 22 ++++- driver/config/provider.go | 13 ++- driver/registry.go | 4 + driver/registry_base.go | 14 +++ flow/consent_types.go | 17 +++- flow/flow.go | 34 ++++--- flow/flow_test.go | 1 + go.mod | 1 + go.sum | 39 ++++++++ internal/httpclient/api/openapi.yaml | 5 + .../docs/AcceptOAuth2LoginRequest.md | 26 +++++ .../model_accept_o_auth2_login_request.go | 37 +++++++ internal/kratos/fake_kratos.go | 35 +++++++ internal/kratos/kratos.go | 69 +++++++++++++ oauth2/fosite_store_test.go | 4 +- .../auth_session-0001.json | 1 + .../auth_session-0002.json | 1 + .../auth_session-0003.json | 1 + .../auth_session-0004.json | 1 + .../auth_session-0005.json | 1 + .../auth_session-0006.json | 1 + .../auth_session-0007.json | 1 + .../auth_session-0008.json | 1 + .../auth_session-0009.json | 1 + .../auth_session-0010.json | 1 + .../auth_session-0011.json | 1 + .../auth_session-0012.json | 1 + .../auth_session-0013.json | 1 + .../auth_session-0014.json | 1 + .../auth_session-0015.json | 1 + .../auth_session-0016.json | 1 + .../auth_session-0017.json | 8 ++ .../hydra_oauth2_flow/challenge-0001.json | 1 + .../hydra_oauth2_flow/challenge-0002.json | 1 + .../hydra_oauth2_flow/challenge-0003.json | 1 + .../hydra_oauth2_flow/challenge-0004.json | 1 + .../hydra_oauth2_flow/challenge-0005.json | 1 + .../hydra_oauth2_flow/challenge-0006.json | 1 + .../hydra_oauth2_flow/challenge-0007.json | 1 + .../hydra_oauth2_flow/challenge-0008.json | 1 + .../hydra_oauth2_flow/challenge-0009.json | 1 + .../hydra_oauth2_flow/challenge-0010.json | 1 + .../hydra_oauth2_flow/challenge-0011.json | 1 + .../hydra_oauth2_flow/challenge-0012.json | 1 + .../hydra_oauth2_flow/challenge-0013.json | 1 + .../hydra_oauth2_flow/challenge-0014.json | 1 + .../hydra_oauth2_flow/challenge-0015.json | 1 + .../hydra_oauth2_flow/challenge-0016.json | 1 + .../hydra_oauth2_flow/challenge-0017.json | 79 +++++++++++++++ persistence/sql/migratest/migration_test.go | 5 +- .../20230809122501000001_testdata.sql | 97 +++++++++++++++++++ ...22501000001_add_kratos_session_id.down.sql | 2 + ...9122501000001_add_kratos_session_id.up.sql | 2 + persistence/sql/persister_consent.go | 58 +++++++++-- persistence/sql/persister_nid_test.go | 22 +++-- spec/api.json | 4 + spec/config.json | 15 +++ spec/swagger.json | 4 + 63 files changed, 645 insertions(+), 61 deletions(-) create mode 100644 internal/kratos/fake_kratos.go create mode 100644 internal/kratos/kratos.go create mode 100644 persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0017.json create mode 100644 persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0017.json create mode 100644 persistence/sql/migratest/testdata/20230809122501000001_testdata.sql create mode 100644 persistence/sql/migrations/20230809122501000001_add_kratos_session_id.down.sql create mode 100644 persistence/sql/migrations/20230809122501000001_add_kratos_session_id.up.sql diff --git a/consent/manager.go b/consent/manager.go index 69b62ed8b9e..45cb00240b7 100644 --- a/consent/manager.go +++ b/consent/manager.go @@ -41,7 +41,7 @@ type ( // Cookie management GetRememberedLoginSession(ctx context.Context, loginSessionFromCookie *flow.LoginSession, id string) (*flow.LoginSession, error) CreateLoginSession(ctx context.Context, session *flow.LoginSession) error - DeleteLoginSession(ctx context.Context, id string) error + DeleteLoginSession(ctx context.Context, id string) (deletedSession *flow.LoginSession, err error) RevokeSubjectLoginSession(ctx context.Context, user string) error ConfirmLoginSession(ctx context.Context, session *flow.LoginSession, id string, authTime time.Time, subject string, remember bool) error diff --git a/consent/manager_test_helpers.go b/consent/manager_test_helpers.go index 2d84bf071d5..588aef92174 100644 --- a/consent/manager_test_helpers.go +++ b/consent/manager_test_helpers.go @@ -324,8 +324,12 @@ func TestHelperNID(r interface { require.NoError(t, err) require.Error(t, t2InvalidNID.ConfirmLoginSession(ctx, &testLS, testLS.ID, time.Now(), testLS.Subject, true)) require.NoError(t, t1ValidNID.ConfirmLoginSession(ctx, &testLS, testLS.ID, time.Now(), testLS.Subject, true)) - require.Error(t, t2InvalidNID.DeleteLoginSession(ctx, testLS.ID)) - require.NoError(t, t1ValidNID.DeleteLoginSession(ctx, testLS.ID)) + ls, err := t2InvalidNID.DeleteLoginSession(ctx, testLS.ID) + require.Error(t, err) + assert.Nil(t, ls) + ls, err = t1ValidNID.DeleteLoginSession(ctx, testLS.ID) + require.NoError(t, err) + assert.Equal(t, testLS.ID, ls.ID) } } @@ -429,8 +433,9 @@ func ManagerTests(deps Deps, m Manager, clientManager client.Manager, fositeMana }, } { t.Run("case=delete-get-"+tc.id, func(t *testing.T) { - err := m.DeleteLoginSession(ctx, tc.id) + ls, err := m.DeleteLoginSession(ctx, tc.id) require.NoError(t, err) + assert.EqualValues(t, tc.id, ls.ID) _, err = m.GetRememberedLoginSession(ctx, nil, tc.id) require.Error(t, err) @@ -1083,7 +1088,8 @@ func ManagerTests(deps Deps, m Manager, clientManager client.Manager, fositeMana require.NoError(t, err) assert.EqualValues(t, expected.ID, result.ID) - require.NoError(t, m.DeleteLoginSession(ctx, s.ID)) + _, err = m.DeleteLoginSession(ctx, s.ID) + require.NoError(t, err) result, err = m.GetConsentRequest(ctx, expected.ID) require.NoError(t, err) diff --git a/consent/registry.go b/consent/registry.go index 447e345ee5b..59e626efaad 100644 --- a/consent/registry.go +++ b/consent/registry.go @@ -9,6 +9,7 @@ import ( "github.com/ory/fosite/handler/openid" "github.com/ory/hydra/v2/aead" "github.com/ory/hydra/v2/client" + "github.com/ory/hydra/v2/internal/kratos" "github.com/ory/hydra/v2/x" ) @@ -17,6 +18,7 @@ type InternalRegistry interface { x.RegistryCookieStore x.RegistryLogger x.HTTPClientProvider + kratos.Provider Registry client.Registry diff --git a/consent/strategy_default.go b/consent/strategy_default.go index 017b584ccff..e03152cb013 100644 --- a/consent/strategy_default.go +++ b/consent/strategy_default.go @@ -313,7 +313,9 @@ func (s *DefaultStrategy) revokeAuthenticationSession(ctx context.Context, w htt return nil } - return s.r.ConsentManager().DeleteLoginSession(r.Context(), sid) + _, err = s.r.ConsentManager().DeleteLoginSession(r.Context(), sid) + + return err } func (s *DefaultStrategy) revokeAuthenticationCookie(w http.ResponseWriter, r *http.Request, ss sessions.Store) (string, error) { @@ -458,6 +460,7 @@ func (s *DefaultStrategy) verifyAuthentication( return nil, fosite.ErrAccessDenied.WithHint("The login session cookie was not found or malformed.") } + loginSession.IdentityProviderSessionID = f.IdentityProviderSessionID if err := s.r.ConsentManager().ConfirmLoginSession(ctx, loginSession, sessionID, time.Time(session.AuthenticatedAt), session.Subject, session.Remember); err != nil { return nil, err } @@ -731,7 +734,8 @@ func (s *DefaultStrategy) generateFrontChannelLogoutURLs(ctx context.Context, su return urls, nil } -func (s *DefaultStrategy) executeBackChannelLogout(ctx context.Context, r *http.Request, subject, sid string) error { +func (s *DefaultStrategy) executeBackChannelLogout(r *http.Request, subject, sid string) error { + ctx := r.Context() clients, err := s.r.ConsentManager().ListUserAuthenticatedClientsWithBackChannelLogout(ctx, subject, sid) if err != nil { return err @@ -1000,8 +1004,9 @@ func (s *DefaultStrategy) issueLogoutVerifier(ctx context.Context, w http.Respon return nil, errorsx.WithStack(ErrAbortOAuth2Request) } -func (s *DefaultStrategy) performBackChannelLogoutAndDeleteSession(_ context.Context, r *http.Request, subject string, sid string) error { - if err := s.executeBackChannelLogout(r.Context(), r, subject, sid); err != nil { +func (s *DefaultStrategy) performBackChannelLogoutAndDeleteSession(r *http.Request, subject string, sid string) error { + ctx := r.Context() + if err := s.executeBackChannelLogout(r, subject, sid); err != nil { return err } @@ -1010,10 +1015,16 @@ func (s *DefaultStrategy) performBackChannelLogoutAndDeleteSession(_ context.Con // // executeBackChannelLogout only fails on system errors so not on URL errors, so this should be fine // even if an upstream URL fails! - if err := s.r.ConsentManager().DeleteLoginSession(r.Context(), sid); errors.Is(err, sqlcon.ErrNoRows) { + if session, err := s.r.ConsentManager().DeleteLoginSession(ctx, sid); errors.Is(err, sqlcon.ErrNoRows) { // This is ok (session probably already revoked), do nothing! } else if err != nil { return err + } else { + innerErr := s.r.Kratos().DisableSession(ctx, session.IdentityProviderSessionID.String()) + if innerErr != nil { + s.r.Logger().WithError(innerErr).WithField("sid", sid).Error("Unable to revoke session in ORY Kratos.") + } + // We don't return the error here because we don't want to break the logout flow if Kratos is down. } return nil @@ -1068,7 +1079,7 @@ func (s *DefaultStrategy) completeLogout(ctx context.Context, w http.ResponseWri return nil, err } - if err := s.performBackChannelLogoutAndDeleteSession(r.Context(), r, lr.Subject, lr.SessionID); err != nil { + if err := s.performBackChannelLogoutAndDeleteSession(r, lr.Subject, lr.SessionID); err != nil { return nil, err } @@ -1105,7 +1116,7 @@ func (s *DefaultStrategy) HandleHeadlessLogout(ctx context.Context, _ http.Respo return lsErr } - if err := s.performBackChannelLogoutAndDeleteSession(r.Context(), r, loginSession.Subject, sid); err != nil { + if err := s.performBackChannelLogoutAndDeleteSession(r, loginSession.Subject, sid); err != nil { return err } diff --git a/consent/strategy_default_test.go b/consent/strategy_default_test.go index 3d8efab8130..75c7682ded3 100644 --- a/consent/strategy_default_test.go +++ b/consent/strategy_default_test.go @@ -8,25 +8,21 @@ import ( "net/http" "net/http/cookiejar" "net/http/httptest" - "testing" - - hydra "github.com/ory/hydra-client-go/v2" - - "github.com/stretchr/testify/require" - - "github.com/ory/fosite/token/jwt" - "github.com/ory/x/urlx" - "net/url" + "testing" "github.com/google/uuid" + "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + "github.com/ory/fosite/token/jwt" + hydra "github.com/ory/hydra-client-go/v2" "github.com/ory/hydra/v2/client" . "github.com/ory/hydra/v2/consent" "github.com/ory/hydra/v2/driver" "github.com/ory/hydra/v2/internal/testhelpers" "github.com/ory/x/ioutilx" + "github.com/ory/x/urlx" ) func checkAndAcceptLoginHandler(t *testing.T, apiClient *hydra.APIClient, subject string, cb func(*testing.T, *hydra.OAuth2LoginRequest, error) hydra.AcceptOAuth2LoginRequest) http.HandlerFunc { diff --git a/consent/strategy_logout_test.go b/consent/strategy_logout_test.go index 3bd5f911811..82005931961 100644 --- a/consent/strategy_logout_test.go +++ b/consent/strategy_logout_test.go @@ -16,6 +16,7 @@ import ( "testing" "time" + "github.com/ory/hydra/v2/internal/kratos" "github.com/ory/x/pointerx" "github.com/stretchr/testify/assert" @@ -35,9 +36,11 @@ import ( func TestLogoutFlows(t *testing.T) { ctx := context.Background() + fakeKratos := kratos.NewFake() reg := internal.NewMockedRegistry(t, &contextx.Default{}) reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, "opaque") reg.Config().MustSet(ctx, config.KeyConsentRequestMaxAge, time.Hour) + reg.WithKratos(fakeKratos) defaultRedirectedMessage := "redirected to default server" postLogoutCallback := func(w http.ResponseWriter, r *http.Request) { @@ -181,7 +184,10 @@ func TestLogoutFlows(t *testing.T) { checkAndAcceptLoginHandler(t, adminApi, subject, func(t *testing.T, res *hydra.OAuth2LoginRequest, err error) hydra.AcceptOAuth2LoginRequest { require.NoError(t, err) //res.Payload.SessionID - return hydra.AcceptOAuth2LoginRequest{Remember: pointerx.Bool(true)} + return hydra.AcceptOAuth2LoginRequest{ + Remember: pointerx.Ptr(true), + IdentityProviderSessionId: pointerx.Ptr(kratos.FakeSessionID), + } }), checkAndAcceptConsentHandler(t, adminApi, func(t *testing.T, res *hydra.OAuth2ConsentRequest, err error) hydra.AcceptOAuth2ConsentRequest { require.NoError(t, err) @@ -476,6 +482,7 @@ func TestLogoutFlows(t *testing.T) { }) t.Run("case=should return to default post logout because session was revoked in browser context", func(t *testing.T) { + fakeKratos.Reset() c := createSampleClient(t) sid := make(chan string) acceptLoginAsAndWatchSid(t, subject, sid) @@ -518,9 +525,13 @@ func TestLogoutFlows(t *testing.T) { assert.NotEmpty(t, res.Request.URL.Query().Get("code")) wg.Wait() + + assert.True(t, fakeKratos.DisableSessionWasCalled) + assert.Equal(t, fakeKratos.LastDisabledSession, kratos.FakeSessionID) }) t.Run("case=should execute backchannel logout in headless flow with sid", func(t *testing.T) { + fakeKratos.Reset() numSidConsumers := 2 sid := make(chan string, numSidConsumers) acceptLoginAsAndWatchSidForConsumers(t, subject, sid, true, numSidConsumers) @@ -535,22 +546,31 @@ func TestLogoutFlows(t *testing.T) { logoutViaHeadlessAndExpectNoContent(t, createBrowserWithSession(t, c), url.Values{"sid": {<-sid}}) backChannelWG.Wait() // we want to ensure that all back channels have been called! + assert.True(t, fakeKratos.DisableSessionWasCalled) + assert.Equal(t, fakeKratos.LastDisabledSession, kratos.FakeSessionID) }) t.Run("case=should logout in headless flow with non-existing sid", func(t *testing.T) { + fakeKratos.Reset() logoutViaHeadlessAndExpectNoContent(t, browserWithoutSession, url.Values{"sid": {"non-existing-sid"}}) + assert.False(t, fakeKratos.DisableSessionWasCalled) }) t.Run("case=should logout in headless flow with session that has remember=false", func(t *testing.T) { + fakeKratos.Reset() sid := make(chan string) acceptLoginAsAndWatchSidForConsumers(t, subject, sid, false, 1) c := createSampleClient(t) logoutViaHeadlessAndExpectNoContent(t, createBrowserWithSession(t, c), url.Values{"sid": {<-sid}}) + assert.True(t, fakeKratos.DisableSessionWasCalled) + assert.Equal(t, fakeKratos.LastDisabledSession, kratos.FakeSessionID) }) t.Run("case=should fail headless logout because neither sid nor subject were provided", func(t *testing.T) { + fakeKratos.Reset() logoutViaHeadlessAndExpectError(t, browserWithoutSession, url.Values{}, `Either 'subject' or 'sid' query parameters need to be defined.`) + assert.False(t, fakeKratos.DisableSessionWasCalled) }) } diff --git a/driver/config/provider.go b/driver/config/provider.go index 274862ed83a..7b2f12f905a 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -80,6 +80,7 @@ const ( KeyPublicURL = "urls.self.public" KeyAdminURL = "urls.self.admin" KeyIssuerURL = "urls.self.issuer" + KeyIdentityProviderAdminURL = "urls.identity_provider.admin_base_url" KeyAccessTokenStrategy = "strategies.access_token" KeyJWTScopeClaimStrategy = "strategies.jwt.scope_claim" KeyDBIgnoreUnknownTableColumns = "db.ignore_unknown_table_columns" @@ -104,8 +105,10 @@ const ( const DSNMemory = "memory" -var _ hasherx.PBKDF2Configurator = (*DefaultProvider)(nil) -var _ hasherx.BCryptConfigurator = (*DefaultProvider)(nil) +var ( + _ hasherx.PBKDF2Configurator = (*DefaultProvider)(nil) + _ hasherx.BCryptConfigurator = (*DefaultProvider)(nil) +) type DefaultProvider struct { l *logrusx.Logger @@ -393,6 +396,12 @@ func (p *DefaultProvider) IssuerURL(ctx context.Context) *url.URL { ) } +func (p *DefaultProvider) KratosAdminURL(ctx context.Context) (*url.URL, bool) { + u := p.getProvider(ctx).RequestURIF(KeyIdentityProviderAdminURL, nil) + + return u, u != nil +} + func (p *DefaultProvider) OAuth2ClientRegistrationURL(ctx context.Context) *url.URL { return p.getProvider(ctx).RequestURIF(KeyOAuth2ClientRegistrationURL, new(url.URL)) } diff --git a/driver/registry.go b/driver/registry.go index d518d8a7f6e..e81d7c35e9a 100644 --- a/driver/registry.go +++ b/driver/registry.go @@ -10,6 +10,7 @@ import ( "go.opentelemetry.io/otel/trace" + "github.com/ory/hydra/v2/internal/kratos" "github.com/ory/x/httprouterx" "github.com/ory/x/popx" @@ -54,6 +55,7 @@ type Registry interface { WithLogger(l *logrusx.Logger) Registry WithTracer(t trace.Tracer) Registry WithTracerWrapper(TracerWrapper) Registry + WithKratos(k kratos.Client) Registry x.HTTPClientProvider GetJWKSFetcherStrategy() fosite.JWKSFetcherStrategy @@ -72,6 +74,8 @@ type Registry interface { x.TracingProvider FlowCipher() *aead.XChaCha20Poly1305 + kratos.Provider + RegisterRoutes(ctx context.Context, admin *httprouterx.RouterAdmin, public *httprouterx.RouterPublic) ClientHandler() *client.Handler KeyHandler() *jwk.Handler diff --git a/driver/registry_base.go b/driver/registry_base.go index 910084ced52..0f85fb50be5 100644 --- a/driver/registry_base.go +++ b/driver/registry_base.go @@ -28,6 +28,7 @@ import ( "github.com/ory/hydra/v2/driver/config" "github.com/ory/hydra/v2/fositex" "github.com/ory/hydra/v2/hsm" + "github.com/ory/hydra/v2/internal/kratos" "github.com/ory/hydra/v2/jwk" "github.com/ory/hydra/v2/oauth2" "github.com/ory/hydra/v2/oauth2/trust" @@ -88,6 +89,7 @@ type RegistryBase struct { hmacs *foauth2.HMACSHAStrategy fc *fositex.Config publicCORS *cors.Cors + kratos kratos.Client } func (m *RegistryBase) GetJWKSFetcherStrategy() fosite.JWKSFetcherStrategy { @@ -201,6 +203,11 @@ func (m *RegistryBase) WithTracerWrapper(wrapper TracerWrapper) Registry { return m.r } +func (m *RegistryBase) WithKratos(k kratos.Client) Registry { + m.kratos = k + return m.r +} + func (m *RegistryBase) Logger() *logrusx.Logger { if m.l == nil { m.l = logrusx.New("Ory Hydra", m.BuildVersion()) @@ -552,3 +559,10 @@ func (m *RegistryBase) HSMContext() hsm.Context { func (m *RegistrySQL) ClientAuthenticator() x.ClientAuthenticator { return m.OAuth2Provider().(*fosite.Fosite) } + +func (m *RegistryBase) Kratos() kratos.Client { + if m.kratos == nil { + m.kratos = kratos.New(m) + } + return m.kratos +} diff --git a/flow/consent_types.go b/flow/consent_types.go index 89e56ef8aa7..9a2666c7867 100644 --- a/flow/consent_types.go +++ b/flow/consent_types.go @@ -42,11 +42,12 @@ type OAuth2RedirectTo struct { // swagger:ignore type LoginSession struct { - ID string `db:"id"` - NID uuid.UUID `db:"nid"` - AuthenticatedAt sqlxx.NullTime `db:"authenticated_at"` - Subject string `db:"subject"` - Remember bool `db:"remember"` + ID string `db:"id"` + NID uuid.UUID `db:"nid"` + AuthenticatedAt sqlxx.NullTime `db:"authenticated_at"` + Subject string `db:"subject"` + IdentityProviderSessionID sqlxx.NullString `db:"identity_provider_session_id"` + Remember bool `db:"remember"` } func (LoginSession) TableName() string { @@ -292,6 +293,12 @@ type HandledLoginRequest struct { // required: true Subject string `json:"subject"` + // IdentityProviderSessionID is the session ID of the end-user that authenticated. + // If specified, we will use this value to propagate the logout. + // + // required: false + IdentityProviderSessionID string `json:"identity_provider_session_id,omitempty"` + // ForceSubjectIdentifier forces the "pairwise" user ID of the end-user that authenticated. The "pairwise" user ID refers to the // (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID // Connect specification. It allows you to set an obfuscated subject ("user") identifier that is unique to the client. diff --git a/flow/flow.go b/flow/flow.go index 0868e7f5f14..7e8eeb077c8 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -136,6 +136,10 @@ type Flow struct { // channel logout. Its value can generally be used to associate consecutive login requests by a certain user. SessionID sqlxx.NullString `db:"login_session_id"` + // IdentityProviderSessionID is the session ID of the end-user that authenticated. + // If specified, we will use this value to propagate the logout. + IdentityProviderSessionID sqlxx.NullString `db:"identity_provider_session_id"` + LoginVerifier string `db:"login_verifier"` LoginCSRF string `db:"login_csrf"` @@ -291,6 +295,7 @@ func (f *Flow) HandleLoginRequest(h *HandledLoginRequest) error { f.ForceSubjectIdentifier = h.ForceSubjectIdentifier f.LoginError = h.Error + f.IdentityProviderSessionID = sqlxx.NullString(h.IdentityProviderSessionID) f.LoginRemember = h.Remember f.LoginRememberFor = h.RememberFor f.LoginExtendSessionLifespan = h.ExtendSessionLifespan @@ -304,20 +309,21 @@ func (f *Flow) HandleLoginRequest(h *HandledLoginRequest) error { func (f *Flow) GetHandledLoginRequest() HandledLoginRequest { return HandledLoginRequest{ - ID: f.ID, - Remember: f.LoginRemember, - RememberFor: f.LoginRememberFor, - ExtendSessionLifespan: f.LoginExtendSessionLifespan, - ACR: f.ACR, - AMR: f.AMR, - Subject: f.Subject, - ForceSubjectIdentifier: f.ForceSubjectIdentifier, - Context: f.Context, - WasHandled: f.LoginWasUsed, - Error: f.LoginError, - LoginRequest: f.GetLoginRequest(), - RequestedAt: f.RequestedAt, - AuthenticatedAt: f.LoginAuthenticatedAt, + ID: f.ID, + Remember: f.LoginRemember, + RememberFor: f.LoginRememberFor, + ExtendSessionLifespan: f.LoginExtendSessionLifespan, + ACR: f.ACR, + AMR: f.AMR, + Subject: f.Subject, + IdentityProviderSessionID: f.IdentityProviderSessionID.String(), + ForceSubjectIdentifier: f.ForceSubjectIdentifier, + Context: f.Context, + WasHandled: f.LoginWasUsed, + Error: f.LoginError, + LoginRequest: f.GetLoginRequest(), + RequestedAt: f.RequestedAt, + AuthenticatedAt: f.LoginAuthenticatedAt, } } diff --git a/flow/flow_test.go b/flow/flow_test.go index 6358c47595b..25a832c780e 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -42,6 +42,7 @@ func (f *Flow) setHandledLoginRequest(r *HandledLoginRequest) { f.ACR = r.ACR f.AMR = r.AMR f.Subject = r.Subject + f.IdentityProviderSessionID = sqlxx.NullString(r.IdentityProviderSessionID) f.ForceSubjectIdentifier = r.ForceSubjectIdentifier f.Context = r.Context f.LoginWasUsed = r.WasHandled diff --git a/go.mod b/go.mod index 34ca96fe293..9a7563fd2ed 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/ory/herodot v0.10.3-0.20230626083119-d7e5192f0d88 github.com/ory/hydra-client-go/v2 v2.1.1 github.com/ory/jsonschema/v3 v3.0.8 + github.com/ory/kratos-client-go v0.13.1 github.com/ory/x v0.0.577 github.com/pborman/uuid v1.2.1 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 9db89dbc948..d269e943f61 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,7 @@ github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WV github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -83,9 +84,11 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -94,9 +97,12 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.3.5 h1:Khtm8K6fTTz/ZCWPzU9Ne3aOW9VyAnj4qIPCJgKtwK0= github.com/cockroachdb/cockroach-go/v2 v2.3.5/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU= github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -104,6 +110,7 @@ github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdB github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/cristalhq/jwt/v4 v4.0.2 h1:g/AD3h0VicDamtlM70GWGElp8kssQEv+5wYd7L9WOhU= github.com/cristalhq/jwt/v4 v4.0.2/go.mod h1:HnYraSNKDRag1DZP92rYHyrjyQHnVEHPNqesmzs+miQ= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -156,6 +163,7 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-faker/faker/v4 v4.1.1 h1:zkxj/JH/aezB4R6cTEMKU7qcVScGhlB3qRtF3D7K+rI= github.com/go-faker/faker/v4 v4.1.1/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0/FUTTYbhg= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -165,6 +173,7 @@ github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyM github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -212,8 +221,11 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= @@ -283,6 +295,7 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -441,6 +454,8 @@ github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LF github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -449,6 +464,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= @@ -487,6 +504,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/laher/mergefs v0.1.1 h1:nV2bTS57vrmbMxeR6uvJpI8LyGl3QHj4bLBZO3aUV58= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -531,6 +549,7 @@ github.com/mikefarah/yq/v4 v4.34.2/go.mod h1:EsGfyWg6sNpnva274ASzb54TJrCBAOKsBgJ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -538,12 +557,17 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nyaruka/phonenumbers v1.1.7 h1:5UUI9hE79Kk0dymSquXbMYB7IlNDNhvu2aNlJpm9et8= github.com/nyaruka/phonenumbers v1.1.7/go.mod h1:DC7jZd321FqUe+qWSNcHi10tyIyGNXGcNbfkPvdp1Vs= @@ -557,6 +581,9 @@ github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYB github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.8 h1:zICRlc+C1XzivLc3nzE+cbJV4LIi8tib6YG0MqC6OqA= github.com/opencontainers/runc v1.1.8/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A= github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM= github.com/ory/analytics-go/v5 v5.0.1 h1:LX8T5B9FN8KZXOtxgN+R3I4THRRVB6+28IKgKBpXmAM= @@ -575,6 +602,8 @@ github.com/ory/herodot v0.10.3-0.20230626083119-d7e5192f0d88 h1:J0CIFKdpUeqKbVMw github.com/ory/herodot v0.10.3-0.20230626083119-d7e5192f0d88/go.mod h1:MMNmY6MG1uB6fnXYFaHoqdV23DTWctlPsmRCeq/2+wc= github.com/ory/jsonschema/v3 v3.0.8 h1:Ssdb3eJ4lDZ/+XnGkvQS/te0p+EkolqwTsDOCxr/FmU= github.com/ory/jsonschema/v3 v3.0.8/go.mod h1:ZPzqjDkwd3QTnb2Z6PAS+OTvBE2x5i6m25wCGx54W/0= +github.com/ory/kratos-client-go v0.13.1 h1:o+pFV9ZRMFSBa4QeNJYbJeLz036UWU4p+7yfKghK+0E= +github.com/ory/kratos-client-go v0.13.1/go.mod h1:hkrFJuHSBQw+qN6Ks0faOAYhAKwtpjvhCZzsQ7g/Ufc= github.com/ory/x v0.0.577 h1:wJRrD2OvEFkbM/cwHrlkSY8VaEO6RUoOnDlUc34YRdk= github.com/ory/x v0.0.577/go.mod h1:aeJFTlvDLGYSABzPS3z5SeLcYC52Ek7uGZiuYGcTMSU= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= @@ -606,6 +635,7 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -622,6 +652,7 @@ github.com/sawadashota/encrypta v0.0.3 h1:NWo2S6oBzZmD/tlm6iH1eYLZA99NsFPvc33Mhk github.com/sawadashota/encrypta v0.0.3/go.mod h1:W3Nja83iH22fOS8sGgKCf4rCehZqLrca1+oQbtFUFck= github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1:0b8DF5kR0PhRoRXDiEEdzrgBc8UqVY4JWLkQJCRsLME= github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/segmentio/analytics-go v3.1.0+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48= github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N++y4= @@ -635,12 +666,14 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= @@ -679,6 +712,7 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -698,8 +732,11 @@ github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9r github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -1190,6 +1227,8 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.3.5/go.mod h1:EGCWefLFQSVFrHGy4J8EtiHCWX5Q8t0yz2Jt9aKkGzU= +gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/httpclient/api/openapi.yaml b/internal/httpclient/api/openapi.yaml index 0841ffa5e9d..6a5d7ab2ec6 100644 --- a/internal/httpclient/api/openapi.yaml +++ b/internal/httpclient/api/openapi.yaml @@ -1989,6 +1989,11 @@ components: \ the proper value, then authentication processes which have id_token_hint\ \ set might fail." type: string + identity_provider_session_id: + description: "IdentityProviderSessionID is the session ID of the end-user\ + \ that authenticated.\nIf specified, we will use this value to propagate\ + \ the logout." + type: string remember: description: "Remember, if set to true, tells ORY Hydra to remember this\ \ user by telling the user agent (browser) to store\na cookie with authentication\ diff --git a/internal/httpclient/docs/AcceptOAuth2LoginRequest.md b/internal/httpclient/docs/AcceptOAuth2LoginRequest.md index 5c41c4923bd..e6f6d554546 100644 --- a/internal/httpclient/docs/AcceptOAuth2LoginRequest.md +++ b/internal/httpclient/docs/AcceptOAuth2LoginRequest.md @@ -9,6 +9,7 @@ Name | Type | Description | Notes **Context** | Pointer to **interface{}** | | [optional] **ExtendSessionLifespan** | Pointer to **bool** | Extend OAuth2 authentication session lifespan If set to `true`, the OAuth2 authentication cookie lifespan is extended. This is for example useful if you want the user to be able to use `prompt=none` continuously. This value can only be set to `true` if the user has an authentication, which is the case if the `skip` value is `true`. | [optional] **ForceSubjectIdentifier** | Pointer to **string** | ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. | [optional] +**IdentityProviderSessionId** | Pointer to **string** | IdentityProviderSessionID is the session ID of the end-user that authenticated. If specified, we will use this value to propagate the logout. | [optional] **Remember** | Pointer to **bool** | Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. | [optional] **RememberFor** | Pointer to **int64** | RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the authorization will be remembered for the duration of the browser session (using a session cookie). | [optional] **Subject** | **string** | Subject is the user ID of the end-user that authenticated. | @@ -167,6 +168,31 @@ SetForceSubjectIdentifier sets ForceSubjectIdentifier field to given value. HasForceSubjectIdentifier returns a boolean if a field has been set. +### GetIdentityProviderSessionId + +`func (o *AcceptOAuth2LoginRequest) GetIdentityProviderSessionId() string` + +GetIdentityProviderSessionId returns the IdentityProviderSessionId field if non-nil, zero value otherwise. + +### GetIdentityProviderSessionIdOk + +`func (o *AcceptOAuth2LoginRequest) GetIdentityProviderSessionIdOk() (*string, bool)` + +GetIdentityProviderSessionIdOk returns a tuple with the IdentityProviderSessionId field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetIdentityProviderSessionId + +`func (o *AcceptOAuth2LoginRequest) SetIdentityProviderSessionId(v string)` + +SetIdentityProviderSessionId sets IdentityProviderSessionId field to given value. + +### HasIdentityProviderSessionId + +`func (o *AcceptOAuth2LoginRequest) HasIdentityProviderSessionId() bool` + +HasIdentityProviderSessionId returns a boolean if a field has been set. + ### GetRemember `func (o *AcceptOAuth2LoginRequest) GetRemember() bool` diff --git a/internal/httpclient/model_accept_o_auth2_login_request.go b/internal/httpclient/model_accept_o_auth2_login_request.go index 36720ee04b3..85c5355b2d6 100644 --- a/internal/httpclient/model_accept_o_auth2_login_request.go +++ b/internal/httpclient/model_accept_o_auth2_login_request.go @@ -25,6 +25,8 @@ type AcceptOAuth2LoginRequest struct { ExtendSessionLifespan *bool `json:"extend_session_lifespan,omitempty"` // ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. ForceSubjectIdentifier *string `json:"force_subject_identifier,omitempty"` + // IdentityProviderSessionID is the session ID of the end-user that authenticated. If specified, we will use this value to propagate the logout. + IdentityProviderSessionId *string `json:"identity_provider_session_id,omitempty"` // Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. Remember *bool `json:"remember,omitempty"` // RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the authorization will be remembered for the duration of the browser session (using a session cookie). @@ -212,6 +214,38 @@ func (o *AcceptOAuth2LoginRequest) SetForceSubjectIdentifier(v string) { o.ForceSubjectIdentifier = &v } +// GetIdentityProviderSessionId returns the IdentityProviderSessionId field value if set, zero value otherwise. +func (o *AcceptOAuth2LoginRequest) GetIdentityProviderSessionId() string { + if o == nil || o.IdentityProviderSessionId == nil { + var ret string + return ret + } + return *o.IdentityProviderSessionId +} + +// GetIdentityProviderSessionIdOk returns a tuple with the IdentityProviderSessionId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AcceptOAuth2LoginRequest) GetIdentityProviderSessionIdOk() (*string, bool) { + if o == nil || o.IdentityProviderSessionId == nil { + return nil, false + } + return o.IdentityProviderSessionId, true +} + +// HasIdentityProviderSessionId returns a boolean if a field has been set. +func (o *AcceptOAuth2LoginRequest) HasIdentityProviderSessionId() bool { + if o != nil && o.IdentityProviderSessionId != nil { + return true + } + + return false +} + +// SetIdentityProviderSessionId gets a reference to the given string and assigns it to the IdentityProviderSessionId field. +func (o *AcceptOAuth2LoginRequest) SetIdentityProviderSessionId(v string) { + o.IdentityProviderSessionId = &v +} + // GetRemember returns the Remember field value if set, zero value otherwise. func (o *AcceptOAuth2LoginRequest) GetRemember() bool { if o == nil || o.Remember == nil { @@ -317,6 +351,9 @@ func (o AcceptOAuth2LoginRequest) MarshalJSON() ([]byte, error) { if o.ForceSubjectIdentifier != nil { toSerialize["force_subject_identifier"] = o.ForceSubjectIdentifier } + if o.IdentityProviderSessionId != nil { + toSerialize["identity_provider_session_id"] = o.IdentityProviderSessionId + } if o.Remember != nil { toSerialize["remember"] = o.Remember } diff --git a/internal/kratos/fake_kratos.go b/internal/kratos/fake_kratos.go new file mode 100644 index 00000000000..9164b742f58 --- /dev/null +++ b/internal/kratos/fake_kratos.go @@ -0,0 +1,35 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package kratos + +import "context" + +type ( + FakeKratos struct { + DisableSessionWasCalled bool + LastDisabledSession string + } +) + +const ( + FakeSessionID = "fake-kratos-session-id" +) + +var _ Client = new(FakeKratos) + +func NewFake() *FakeKratos { + return &FakeKratos{} +} + +func (f *FakeKratos) DisableSession(ctx context.Context, identityProviderSessionID string) error { + f.DisableSessionWasCalled = true + f.LastDisabledSession = identityProviderSessionID + + return nil +} + +func (f *FakeKratos) Reset() { + f.DisableSessionWasCalled = false + f.LastDisabledSession = "" +} diff --git a/internal/kratos/kratos.go b/internal/kratos/kratos.go new file mode 100644 index 00000000000..1cafc950ded --- /dev/null +++ b/internal/kratos/kratos.go @@ -0,0 +1,69 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package kratos + +import ( + "context" + "net/url" + + "go.opentelemetry.io/otel/attribute" + + "github.com/ory/hydra/v2/driver/config" + "github.com/ory/hydra/v2/x" + client "github.com/ory/kratos-client-go" + "github.com/ory/x/otelx" +) + +type ( + dependencies interface { + config.Provider + x.HTTPClientProvider + x.TracingProvider + } + Provider interface { + Kratos() Client + } + Client interface { + DisableSession(ctx context.Context, identityProviderSessionID string) error + } + Default struct { + dependencies + } +) + +func New(d dependencies) Client { + return &Default{dependencies: d} +} + +func (k *Default) DisableSession(ctx context.Context, identityProviderSessionID string) (err error) { + ctx, span := k.Tracer(ctx).Tracer().Start(ctx, "kratos.DisableSession") + otelx.End(span, &err) + + adminURL, ok := k.Config().KratosAdminURL(ctx) + if !ok { + span.SetAttributes(attribute.Bool("skipped", true)) + span.SetAttributes(attribute.String("reason", "kratos admin url not set")) + return nil + } + + if identityProviderSessionID == "" { + span.SetAttributes(attribute.Bool("skipped", true)) + span.SetAttributes(attribute.String("reason", "kratos session ID is empty")) + return nil + } + + kratos := client.NewAPIClient(k.clientConfiguration(ctx, adminURL)) + _, err = kratos.IdentityApi.DisableSession(ctx, identityProviderSessionID).Execute() + + return err + +} + +func (k *Default) clientConfiguration(ctx context.Context, adminURL *url.URL) *client.Configuration { + configuration := client.NewConfiguration() + configuration.Servers = client.ServerConfigurations{{URL: adminURL.String()}} + configuration.HTTPClient = k.HTTPClient(ctx).StandardClient() + + return configuration +} diff --git a/oauth2/fosite_store_test.go b/oauth2/fosite_store_test.go index 767f33444d5..f1e0a03c65a 100644 --- a/oauth2/fosite_store_test.go +++ b/oauth2/fosite_store_test.go @@ -52,7 +52,7 @@ func setupRegistries(t *testing.T) { } func TestManagers(t *testing.T) { - ctx := context.TODO() + ctx := context.Background() tests := []struct { name string enableSessionEncrypted bool @@ -67,7 +67,7 @@ func TestManagers(t *testing.T) { }, } for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { + t.Run("suite="+tc.name, func(t *testing.T) { setupRegistries(t) require.NoError(t, registries["memory"].ClientManager().CreateClient(context.Background(), &client.Client{LegacyClientID: "foobar"})) // this is a workaround because the client is not being created for memory store by test helpers. diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0001.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0001.json index a642f6606bf..eb6ec18517e 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0001.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0001.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0001", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0002.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0002.json index 65d132ab888..130b019ccff 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0002.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0002.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0002", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0003.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0003.json index dfa9ae21e86..c6a2c35901f 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0003.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0003.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0003", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0004.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0004.json index 4411c2064a5..5da2bd40758 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0004.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0004.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0004", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0005.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0005.json index cba9dcf6125..0b3c6f8f4d7 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0005.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0005.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0005", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0006.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0006.json index 2081943455f..aa63cb104bb 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0006.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0006.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0006", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0007.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0007.json index c701732aac3..07b96a3d592 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0007.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0007.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0007", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0008.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0008.json index b8fc116b728..2e781eeda86 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0008.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0008.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0008", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0009.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0009.json index 57cee5ecb1e..204d0fffaf2 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0009.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0009.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0009", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0010.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0010.json index 3e6b4d5fa10..02608ae43f4 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0010.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0010.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0010", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0011.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0011.json index 1344d7158fb..33d7187e6e2 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0011.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0011.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0011", + "IdentityProviderSessionID": "", "Remember": false } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0012.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0012.json index 876277e2440..5269cc061b2 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0012.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0012.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0012", + "IdentityProviderSessionID": "", "Remember": false } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0013.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0013.json index 404a1b4fe37..b4a2ee9f13d 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0013.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0013.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0013", + "IdentityProviderSessionID": "", "Remember": false } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0014.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0014.json index 472bef86c13..a9bb155e485 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0014.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0014.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0014", + "IdentityProviderSessionID": "", "Remember": false } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0015.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0015.json index 1f82a76af6b..c88a473e7cc 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0015.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0015.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0015", + "IdentityProviderSessionID": "", "Remember": false } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0016.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0016.json index e4e49d49af9..e2d69a9796b 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0016.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0016.json @@ -3,5 +3,6 @@ "NID": "00000000-0000-0000-0000-000000000000", "AuthenticatedAt": null, "Subject": "subject-0016", + "IdentityProviderSessionID": "", "Remember": true } diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0017.json b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0017.json new file mode 100644 index 00000000000..aea0119e354 --- /dev/null +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_authentication_session/auth_session-0017.json @@ -0,0 +1,8 @@ +{ + "ID": "auth_session-0017", + "NID": "00000000-0000-0000-0000-000000000000", + "AuthenticatedAt": null, + "Subject": "subject-0017", + "IdentityProviderSessionID": "identity_provider_session_id-0017", + "Remember": true +} diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0001.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0001.json index d89f26c7d42..c790dc021e7 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0001.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0001.json @@ -14,6 +14,7 @@ "ClientID": "", "RequestURL": "http://request/0001", "SessionID": "", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0001", "LoginCSRF": "csrf-0001", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0002.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0002.json index 369ba83ba25..1917ef94c5f 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0002.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0002.json @@ -14,6 +14,7 @@ "ClientID": "", "RequestURL": "http://request/0002", "SessionID": "", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0002", "LoginCSRF": "csrf-0002", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0003.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0003.json index 66718c0ba27..39ca512a15a 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0003.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0003.json @@ -14,6 +14,7 @@ "ClientID": "", "RequestURL": "http://request/0003", "SessionID": "auth_session-0003", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0003", "LoginCSRF": "csrf-0003", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0004.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0004.json index e707616aa87..b3dc1b958e0 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0004.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0004.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0004", "SessionID": "auth_session-0004", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0004", "LoginCSRF": "csrf-0004", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0005.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0005.json index fcc4760db32..2a642cae89b 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0005.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0005.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0005", "SessionID": "auth_session-0005", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0005", "LoginCSRF": "csrf-0005", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0006.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0006.json index 825ca5b9b00..89bfd52e6e3 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0006.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0006.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0006", "SessionID": "auth_session-0006", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0006", "LoginCSRF": "csrf-0006", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0007.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0007.json index 1d20de4190f..2c84d09ad47 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0007.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0007.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0007", "SessionID": "auth_session-0007", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0007", "LoginCSRF": "csrf-0007", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0008.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0008.json index 3ed3dad5245..d2e7d2f2fdd 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0008.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0008.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0008", "SessionID": "auth_session-0008", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0008", "LoginCSRF": "csrf-0008", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0009.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0009.json index 61f8bbabf0c..6d3e70d5e37 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0009.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0009.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0009", "SessionID": "auth_session-0009", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0009", "LoginCSRF": "csrf-0009", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0010.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0010.json index a886dd0aefe..6b8f3cf7a10 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0010.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0010.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0010", "SessionID": "auth_session-0010", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0010", "LoginCSRF": "csrf-0010", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0011.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0011.json index dda3212a8d7..5b11c1941d1 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0011.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0011.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0011", "SessionID": "auth_session-0011", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0011", "LoginCSRF": "csrf-0011", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0012.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0012.json index d6491837a10..b648871cda5 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0012.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0012.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0012", "SessionID": "auth_session-0012", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0012", "LoginCSRF": "csrf-0012", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0013.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0013.json index 89ca9f7daf4..1f28bb67647 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0013.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0013.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0013", "SessionID": "auth_session-0013", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0013", "LoginCSRF": "csrf-0013", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0014.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0014.json index d020259b581..3e2dd7bb444 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0014.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0014.json @@ -16,6 +16,7 @@ "ClientID": "", "RequestURL": "http://request/0014", "SessionID": "auth_session-0014", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0014", "LoginCSRF": "csrf-0014", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0015.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0015.json index 78ee82f16d5..5723154839a 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0015.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0015.json @@ -18,6 +18,7 @@ "ClientID": "", "RequestURL": "http://request/0015", "SessionID": "auth_session-0015", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0015", "LoginCSRF": "csrf-0015", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0016.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0016.json index e3bddee39a1..e653059fe46 100644 --- a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0016.json +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0016.json @@ -18,6 +18,7 @@ "ClientID": "", "RequestURL": "http://request/0016", "SessionID": "auth_session-0016", + "IdentityProviderSessionID": "", "LoginVerifier": "verifier-0016", "LoginCSRF": "csrf-0016", "LoginInitializedAt": null, diff --git a/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0017.json b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0017.json new file mode 100644 index 00000000000..fd65dab7164 --- /dev/null +++ b/persistence/sql/migratest/fixtures/hydra_oauth2_flow/challenge-0017.json @@ -0,0 +1,79 @@ +{ + "ID": "challenge-0017", + "NID": "00000000-0000-0000-0000-000000000000", + "RequestedScope": [ + "requested_scope-0016_1", + "requested_scope-0016_2" + ], + "RequestedAudience": [ + "requested_audience-0016_1", + "requested_audience-0016_2" + ], + "LoginSkip": true, + "Subject": "subject-0017", + "OpenIDConnectContext": { + "display": "display-0017" + }, + "Client": null, + "ClientID": "", + "RequestURL": "http://request/0017", + "SessionID": "auth_session-0017", + "IdentityProviderSessionID": "identity_provider_session_id-0017", + "LoginVerifier": "verifier-0017", + "LoginCSRF": "csrf-0017", + "LoginInitializedAt": null, + "RequestedAt": "0001-01-01T00:00:00Z", + "State": 128, + "LoginRemember": true, + "LoginRememberFor": 15, + "LoginExtendSessionLifespan": true, + "ACR": "acr-0017", + "AMR": [ + "amr-0017-1", + "amr-0017-2" + ], + "ForceSubjectIdentifier": "force_subject_id-0017", + "Context": { + "context": "0017" + }, + "LoginWasUsed": true, + "LoginError": { + "error": "", + "error_description": "", + "error_hint": "", + "status_code": 0, + "error_debug": "", + "valid": false + }, + "LoginAuthenticatedAt": null, + "ConsentChallengeID": "challenge-0017", + "ConsentSkip": true, + "ConsentVerifier": "verifier-0017", + "ConsentCSRF": "csrf-0017", + "GrantedScope": [ + "granted_scope-0016_1", + "granted_scope-0016_2" + ], + "GrantedAudience": [ + "granted_audience-0016_1", + "granted_audience-0016_2" + ], + "ConsentRemember": true, + "ConsentRememberFor": 15, + "ConsentHandledAt": null, + "ConsentWasHandled": true, + "ConsentError": { + "error": "", + "error_description": "", + "error_hint": "", + "status_code": 0, + "error_debug": "", + "valid": false + }, + "SessionIDToken": { + "session_id_token-0017": "0017" + }, + "SessionAccessToken": { + "session_access_token-0017": "0017" + } +} diff --git a/persistence/sql/migratest/migration_test.go b/persistence/sql/migratest/migration_test.go index 7c4db0c81d2..02047008c1f 100644 --- a/persistence/sql/migratest/migration_test.go +++ b/persistence/sql/migratest/migration_test.go @@ -60,7 +60,6 @@ func CompareWithFixture(t *testing.T, actual interface{}, prefix string, id stri } func TestMigrations(t *testing.T) { - //pop.Debug = true connections := make(map[string]*pop.Connection, 1) if testing.Short() { @@ -133,7 +132,7 @@ func TestMigrations(t *testing.T) { flows := []flow.Flow{} require.NoError(t, c.All(&flows)) - require.Equal(t, 16, len(flows)) + require.Equal(t, 17, len(flows)) t.Run("case=hydra_oauth2_flow", func(t *testing.T) { for _, f := range flows { @@ -145,7 +144,7 @@ func TestMigrations(t *testing.T) { t.Run("case=hydra_oauth2_authentication_session", func(t *testing.T) { ss := []flow.LoginSession{} c.All(&ss) - require.Equal(t, 16, len(ss)) + require.Equal(t, 17, len(ss)) for _, s := range ss { testhelpersuuid.AssertUUID(t, &s.NID) diff --git a/persistence/sql/migratest/testdata/20230809122501000001_testdata.sql b/persistence/sql/migratest/testdata/20230809122501000001_testdata.sql new file mode 100644 index 00000000000..472700be3db --- /dev/null +++ b/persistence/sql/migratest/testdata/20230809122501000001_testdata.sql @@ -0,0 +1,97 @@ +INSERT INTO hydra_oauth2_authentication_session ( + id, + nid, + authenticated_at, + subject, + remember, + identity_provider_session_id +) VALUES ( + 'auth_session-0017', + (SELECT id FROM networks LIMIT 1), + CURRENT_TIMESTAMP, + 'subject-0017', + true, + 'identity_provider_session_id-0017' +); + +INSERT INTO hydra_oauth2_flow ( + login_challenge, + nid, + requested_scope, + login_verifier, + login_csrf, + subject, + request_url, + login_skip, + client_id, + requested_at, + oidc_context, + login_session_id, + requested_at_audience, + login_initialized_at, + state, + login_remember, + login_remember_for, + login_error, + acr, + login_authenticated_at, + login_was_used, + forced_subject_identifier, + context, + amr, + consent_challenge_id, + consent_verifier, + consent_skip, + consent_csrf, + granted_scope, + consent_remember, + consent_remember_for, + consent_error, + session_access_token, + session_id_token, + consent_was_used, + granted_at_audience, + consent_handled_at, + login_extend_session_lifespan, + identity_provider_session_id +) VALUES ( + 'challenge-0017', + (SELECT id FROM networks LIMIT 1), + '["requested_scope-0016_1","requested_scope-0016_2"]', + 'verifier-0017', + 'csrf-0017', + 'subject-0017', + 'http://request/0017', + true, + 'client-21', + CURRENT_TIMESTAMP, + '{"display": "display-0017"}', + 'auth_session-0017', + '["requested_audience-0016_1","requested_audience-0016_2"]', + CURRENT_TIMESTAMP, + 128, + true, + 15, + '{}', + 'acr-0017', + CURRENT_TIMESTAMP, + true, + 'force_subject_id-0017', + '{"context": "0017"}', + '["amr-0017-1","amr-0017-2"]', + 'challenge-0017', + 'verifier-0017', + true, + 'csrf-0017', + '["granted_scope-0016_1","granted_scope-0016_2"]', + true, + 15, + '{}', + '{"session_access_token-0017": "0017"}', + '{"session_id_token-0017": "0017"}', + true, + '["granted_audience-0016_1","granted_audience-0016_2"]', + CURRENT_TIMESTAMP, + true, + 'identity_provider_session_id-0017' +); diff --git a/persistence/sql/migrations/20230809122501000001_add_kratos_session_id.down.sql b/persistence/sql/migrations/20230809122501000001_add_kratos_session_id.down.sql new file mode 100644 index 00000000000..b5ab0899249 --- /dev/null +++ b/persistence/sql/migrations/20230809122501000001_add_kratos_session_id.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE hydra_oauth2_flow DROP COLUMN identity_provider_session_id; +ALTER TABLE hydra_oauth2_authentication_session DROP COLUMN identity_provider_session_id; \ No newline at end of file diff --git a/persistence/sql/migrations/20230809122501000001_add_kratos_session_id.up.sql b/persistence/sql/migrations/20230809122501000001_add_kratos_session_id.up.sql new file mode 100644 index 00000000000..1d39c457105 --- /dev/null +++ b/persistence/sql/migrations/20230809122501000001_add_kratos_session_id.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE hydra_oauth2_flow ADD COLUMN identity_provider_session_id VARCHAR(40); +ALTER TABLE hydra_oauth2_authentication_session ADD COLUMN identity_provider_session_id VARCHAR(40); \ No newline at end of file diff --git a/persistence/sql/persister_consent.go b/persistence/sql/persister_consent.go index bd401d56423..e92be0fff01 100644 --- a/persistence/sql/persister_consent.go +++ b/persistence/sql/persister_consent.go @@ -14,6 +14,7 @@ import ( "github.com/gofrs/uuid" "github.com/ory/hydra/v2/oauth2/flowctx" + "github.com/ory/x/otelx" "github.com/ory/x/sqlxx" "github.com/ory/x/errorsx" @@ -450,16 +451,59 @@ func (p *Persister) CreateLoginSession(ctx context.Context, session *flow.LoginS return nil } -func (p *Persister) DeleteLoginSession(ctx context.Context, id string) error { +func (p *Persister) DeleteLoginSession(ctx context.Context, id string) (deletedSession *flow.LoginSession, err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.DeleteLoginSession") - defer span.End() + defer otelx.End(span, &err) - count, err := p.Connection(ctx).RawQuery("DELETE FROM hydra_oauth2_authentication_session WHERE id=? AND nid=?", id, p.NetworkID(ctx)).ExecWithCount() - if count == 0 { - return errorsx.WithStack(x.ErrNotFound) - } else { - return sqlcon.HandleError(err) + if p.Connection(ctx).Dialect.Name() == "mysql" { + // MySQL does not support RETURNING. + return p.mySQLDeleteLoginSession(ctx, id) + } + + var session flow.LoginSession + + err = p.Connection(ctx).RawQuery( + `DELETE FROM hydra_oauth2_authentication_session + WHERE id = ? AND nid = ? + RETURNING *`, + id, + p.NetworkID(ctx), + ).First(&session) + if err != nil { + return nil, sqlcon.HandleError(err) + } + + return &session, nil +} + +func (p *Persister) mySQLDeleteLoginSession(ctx context.Context, id string) (*flow.LoginSession, error) { + var session flow.LoginSession + + err := p.Connection(ctx).Transaction(func(tx *pop.Connection) error { + err := tx.RawQuery(` +SELECT * FROM hydra_oauth2_authentication_session +WHERE id = ? AND nid = ?`, + id, + p.NetworkID(ctx), + ).First(&session) + if err != nil { + return err + } + + return p.Connection(ctx).RawQuery(` +DELETE FROM hydra_oauth2_authentication_session +WHERE id = ? AND nid = ?`, + id, + p.NetworkID(ctx), + ).Exec() + }) + + if err != nil { + return nil, sqlcon.HandleError(err) } + + return &session, nil + } func (p *Persister) FindGrantedAndRememberedConsentRequests(ctx context.Context, client, subject string) (rs []flow.AcceptOAuth2ConsentRequest, err error) { diff --git a/persistence/sql/persister_nid_test.go b/persistence/sql/persister_nid_test.go index 83fad7c1452..807656aa2d4 100644 --- a/persistence/sql/persister_nid_test.go +++ b/persistence/sql/persister_nid_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/ory/hydra/v2/persistence" "github.com/ory/x/uuidx" @@ -188,7 +190,7 @@ func (s *PersisterTestSuite) TestConfirmLoginSession() { require.NoError(t, r.Persister().CreateLoginSession(s.t1, ls)) // Expects the login session to be confirmed in the correct context. - require.NoError(t, r.Persister().ConfirmLoginSession(s.t1, ls, ls.ID, time.Now(), ls.Subject, !ls.Remember)) + require.NoError(t, r.Persister().ConfirmLoginSession(s.t1, ls, ls.ID, time.Now().UTC(), ls.Subject, !ls.Remember)) actual := &flow.LoginSession{} require.NoError(t, r.Persister().Connection(context.Background()).Find(actual, ls.ID)) exp, _ := json.Marshal(ls) @@ -197,7 +199,7 @@ func (s *PersisterTestSuite) TestConfirmLoginSession() { // Can't find the login session in the wrong context. require.ErrorIs(t, - r.Persister().ConfirmLoginSession(s.t2, ls, ls.ID, time.Now(), ls.Subject, !ls.Remember), + r.Persister().ConfirmLoginSession(s.t2, ls, ls.ID, time.Now().UTC(), ls.Subject, !ls.Remember), x.ErrNotFound, ) }) @@ -632,14 +634,22 @@ func (s *PersisterTestSuite) TestDeleteLoginSession() { t := s.T() for k, r := range s.registries { t.Run(k, func(t *testing.T) { - ls := flow.LoginSession{ID: uuid.Must(uuid.NewV4()).String(), Remember: true} + ls := flow.LoginSession{ + ID: uuid.Must(uuid.NewV4()).String(), + Remember: true, + IdentityProviderSessionID: sqlxx.NullString(uuid.Must(uuid.NewV4()).String()), + } persistLoginSession(s.t1, t, r.Persister(), &ls) - require.Error(t, r.Persister().DeleteLoginSession(s.t2, ls.ID)) - _, err := r.Persister().GetRememberedLoginSession(s.t1, nil, ls.ID) + deletedLS, err := r.Persister().DeleteLoginSession(s.t2, ls.ID) + require.Error(t, err) + assert.Nil(t, deletedLS) + _, err = r.Persister().GetRememberedLoginSession(s.t1, nil, ls.ID) require.NoError(t, err) - require.NoError(t, r.Persister().DeleteLoginSession(s.t1, ls.ID)) + deletedLS, err = r.Persister().DeleteLoginSession(s.t1, ls.ID) + require.NoError(t, err) + assert.Equal(t, ls, *deletedLS) _, err = r.Persister().GetRememberedLoginSession(s.t1, nil, ls.ID) require.Error(t, err) }) diff --git a/spec/api.json b/spec/api.json index 0c4a3d7077f..1e6b8cd225d 100644 --- a/spec/api.json +++ b/spec/api.json @@ -207,6 +207,10 @@ "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", "type": "string" }, + "identity_provider_session_id": { + "description": "IdentityProviderSessionID is the session ID of the end-user that authenticated.\nIf specified, we will use this value to propagate the logout.", + "type": "string" + }, "remember": { "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", "type": "boolean" diff --git a/spec/config.json b/spec/config.json index f0069762447..dc96a1f56cd 100644 --- a/spec/config.json +++ b/spec/config.json @@ -760,6 +760,21 @@ "https://my-example.app/logout-successful", "/ui" ] + }, + "identity_provider": { + "type": "object", + "additionalProperties": false, + "properties": { + "admin_base_url": { + "type": "string", + "title": "The admin URL of the ORY Kratos instance.", + "description": "If set, ORY Hydra will use this URL to log out the user in addition to removing the Hydra session.", + "format": "uri-reference", + "examples": [ + "https://kratos.example.com/admin" + ] + } + } } } }, diff --git a/spec/swagger.json b/spec/swagger.json index 48e19616d07..9fda613b50e 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -2237,6 +2237,10 @@ "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", "type": "string" }, + "identity_provider_session_id": { + "description": "IdentityProviderSessionID is the session ID of the end-user that authenticated.\nIf specified, we will use this value to propagate the logout.", + "type": "string" + }, "remember": { "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", "type": "boolean"