From 46ecf9a778423d64d8c6931cbfc164b3042390fb Mon Sep 17 00:00:00 2001 From: "minami.yoshihiko" Date: Tue, 7 May 2024 19:12:12 +0900 Subject: [PATCH 1/5] add default signature algorithm --- pkg/oidc/verifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/oidc/verifier.go b/pkg/oidc/verifier.go index 410b3839..cb666762 100644 --- a/pkg/oidc/verifier.go +++ b/pkg/oidc/verifier.go @@ -186,7 +186,7 @@ func toJoseSignatureAlgorithms(algorithms []string) []jose.SignatureAlgorithm { out[i] = jose.SignatureAlgorithm(algorithms[i]) } if len(out) == 0 { - out = append(out, jose.RS256) + out = append(out, jose.RS256, jose.ES256, jose.PS256) } return out } From 32ff570f7c3cf6e34a6bad7894b8fae6b2a91ef9 Mon Sep 17 00:00:00 2001 From: "minami.yoshihiko" Date: Sat, 15 Feb 2025 17:58:14 +0900 Subject: [PATCH 2/5] implements session_state in auth_request.go --- pkg/oidc/error.go | 9 ++++++++- pkg/op/auth_request.go | 26 ++++++++++++++++++++------ pkg/op/error.go | 12 ++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/pkg/oidc/error.go b/pkg/oidc/error.go index 1100f735..d93cf44c 100644 --- a/pkg/oidc/error.go +++ b/pkg/oidc/error.go @@ -133,6 +133,7 @@ type Error struct { ErrorType errorType `json:"error" schema:"error"` Description string `json:"error_description,omitempty" schema:"error_description,omitempty"` State string `json:"state,omitempty" schema:"state,omitempty"` + SessionState string `json:"session_state,omitempty" schema:"session_state,omitempty"` redirectDisabled bool `schema:"-"` returnParent bool `schema:"-"` } @@ -142,11 +143,13 @@ func (e *Error) MarshalJSON() ([]byte, error) { Error errorType `json:"error"` ErrorDescription string `json:"error_description,omitempty"` State string `json:"state,omitempty"` + SessionState string `json:"session_state,omitempty"` Parent string `json:"parent,omitempty"` }{ Error: e.ErrorType, ErrorDescription: e.Description, State: e.State, + SessionState: e.SessionState, } if e.returnParent { m.Parent = e.Parent.Error() @@ -176,7 +179,8 @@ func (e *Error) Is(target error) bool { } return e.ErrorType == t.ErrorType && (e.Description == t.Description || t.Description == "") && - (e.State == t.State || t.State == "") + (e.State == t.State || t.State == "") && + (e.SessionState == t.SessionState || t.SessionState == "") } func (e *Error) WithParent(err error) *Error { @@ -242,6 +246,9 @@ func (e *Error) LogValue() slog.Value { if e.State != "" { attrs = append(attrs, slog.String("state", e.State)) } + if e.SessionState != "" { + attrs = append(attrs, slog.String("session_state", e.SessionState)) + } if e.redirectDisabled { attrs = append(attrs, slog.Bool("redirect_disabled", e.redirectDisabled)) } diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index d6db62bb..e77502ba 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -38,6 +38,13 @@ type AuthRequest interface { Done() bool } +// AuthRequestSessionState should be implemented if OpenID Connect Session Management is supported +type AuthRequestSessionState interface { + // GetSessionState returns session_state. + // session_state is related to OpenID Connect Session Management. + GetSessionState() string +} + type Authorizer interface { Storage() Storage Decoder() httphelper.Decoder @@ -103,8 +110,8 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { } return ValidateAuthRequestClient(ctx, authReq, client, verifier) } - if validater, ok := authorizer.(AuthorizeValidator); ok { - validation = validater.ValidateAuthRequest + if validator, ok := authorizer.(AuthorizeValidator); ok { + validation = validator.ValidateAuthRequest } userID, err := validation(ctx, authReq, authorizer.Storage(), authorizer.IDTokenHintVerifier(ctx)) if err != nil { @@ -481,12 +488,19 @@ func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthReques AuthRequestError(w, r, authReq, err, authorizer) return } + var sessionState string + authRequestSessionState, ok := authReq.(AuthRequestSessionState) + if ok { + sessionState = authRequestSessionState.GetSessionState() + } codeResponse := struct { - Code string `schema:"code"` - State string `schema:"state,omitempty"` + Code string `schema:"code"` + State string `schema:"state,omitempty"` + SessionState string `schema:"session_state,omitempty"` }{ - Code: code, - State: authReq.GetState(), + Code: code, + State: authReq.GetState(), + SessionState: sessionState, } if authReq.GetResponseMode() == oidc.ResponseModeFormPost { diff --git a/pkg/op/error.go b/pkg/op/error.go index 44b17988..d57da837 100644 --- a/pkg/op/error.go +++ b/pkg/op/error.go @@ -46,6 +46,12 @@ func AuthRequestError(w http.ResponseWriter, r *http.Request, authReq ErrAuthReq return } e.State = authReq.GetState() + var sessionState string + authRequestSessionState, ok := authReq.(AuthRequestSessionState) + if ok { + sessionState = authRequestSessionState.GetSessionState() + } + e.SessionState = sessionState var responseMode oidc.ResponseMode if rm, ok := authReq.(interface{ GetResponseMode() oidc.ResponseMode }); ok { responseMode = rm.GetResponseMode() @@ -92,6 +98,12 @@ func TryErrorRedirect(ctx context.Context, authReq ErrAuthRequest, parent error, } e.State = authReq.GetState() + var sessionState string + authRequestSessionState, ok := authReq.(AuthRequestSessionState) + if ok { + sessionState = authRequestSessionState.GetSessionState() + } + e.SessionState = sessionState var responseMode oidc.ResponseMode if rm, ok := authReq.(interface{ GetResponseMode() oidc.ResponseMode }); ok { responseMode = rm.GetResponseMode() From 59bd5643e64d40c15c7fce95beec81abba019a29 Mon Sep 17 00:00:00 2001 From: "minami.yoshihiko" Date: Sat, 15 Feb 2025 18:15:51 +0900 Subject: [PATCH 3/5] add test --- example/server/storage/oidc.go | 9 +++++++++ pkg/op/auth_request_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/example/server/storage/oidc.go b/example/server/storage/oidc.go index 22c0295f..c04877f1 100644 --- a/example/server/storage/oidc.go +++ b/example/server/storage/oidc.go @@ -164,6 +164,15 @@ func authRequestToInternal(authReq *oidc.AuthRequest, userID string) *AuthReques } } +type AuthRequestWithSessionState struct { + *AuthRequest + SessionState string +} + +func (a *AuthRequestWithSessionState) GetSessionState() string { + return a.SessionState +} + type OIDCCodeChallenge struct { Challenge string Method string diff --git a/pkg/op/auth_request_test.go b/pkg/op/auth_request_test.go index 765e6023..4878f5e4 100644 --- a/pkg/op/auth_request_test.go +++ b/pkg/op/auth_request_test.go @@ -1090,6 +1090,34 @@ func TestAuthResponseCode(t *testing.T) { wantBody: "", }, }, + { + name: "success with state and session_state", + args: args{ + authReq: &storage.AuthRequestWithSessionState{ + AuthRequest: &storage.AuthRequest{ + ID: "id1", + TransferState: "state1", + }, + SessionState: "session_state1", + }, + authorizer: func(t *testing.T) op.Authorizer { + ctrl := gomock.NewController(t) + storage := mock.NewMockStorage(ctrl) + storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") + + authorizer := mock.NewMockAuthorizer(ctrl) + authorizer.EXPECT().Storage().Return(storage) + authorizer.EXPECT().Crypto().Return(&mockCrypto{}) + authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) + return authorizer + }, + }, + res: res{ + wantCode: http.StatusFound, + wantLocationHeader: "/auth/callback/?code=id1&session_state=session_state1&state=state1", + wantBody: "", + }, + }, { name: "success without state", // reproduce issue #415 args: args{ From 74218769cb7a682e0c17ffa67f04eced86703739 Mon Sep 17 00:00:00 2001 From: minami yoshihiko Date: Sun, 23 Feb 2025 14:51:42 +0900 Subject: [PATCH 4/5] Update pkg/op/auth_request.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit link to the standard Co-authored-by: Tim Möhlmann --- pkg/op/auth_request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index e77502ba..82f1b580 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -38,7 +38,7 @@ type AuthRequest interface { Done() bool } -// AuthRequestSessionState should be implemented if OpenID Connect Session Management is supported +// AuthRequestSessionState should be implemented if [OpenID Connect Session Management](https://openid.net/specs/openid-connect-session-1_0.html) is supported type AuthRequestSessionState interface { // GetSessionState returns session_state. // session_state is related to OpenID Connect Session Management. From 689d6ed6758d79c5826c33352470db4214d4b0c8 Mon Sep 17 00:00:00 2001 From: "minami.yoshihiko" Date: Sun, 23 Feb 2025 15:24:59 +0900 Subject: [PATCH 5/5] add check_session_iframe --- pkg/op/config.go | 1 + pkg/op/discovery.go | 1 + pkg/op/mock/configuration.mock.go | 14 ++++++++++++++ pkg/op/op.go | 4 ++++ 4 files changed, 20 insertions(+) diff --git a/pkg/op/config.go b/pkg/op/config.go index 2fcede0f..b2717654 100644 --- a/pkg/op/config.go +++ b/pkg/op/config.go @@ -30,6 +30,7 @@ type Configuration interface { EndSessionEndpoint() *Endpoint KeysEndpoint() *Endpoint DeviceAuthorizationEndpoint() *Endpoint + CheckSessionIframe() *Endpoint AuthMethodPostSupported() bool CodeMethodS256Supported() bool diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go index e30a5a4b..7aa7cf72 100644 --- a/pkg/op/discovery.go +++ b/pkg/op/discovery.go @@ -45,6 +45,7 @@ func CreateDiscoveryConfig(ctx context.Context, config Configuration, storage Di EndSessionEndpoint: config.EndSessionEndpoint().Absolute(issuer), JwksURI: config.KeysEndpoint().Absolute(issuer), DeviceAuthorizationEndpoint: config.DeviceAuthorizationEndpoint().Absolute(issuer), + CheckSessionIframe: config.CheckSessionIframe().Absolute(issuer), ScopesSupported: Scopes(config), ResponseTypesSupported: ResponseTypes(config), GrantTypesSupported: GrantTypes(config), diff --git a/pkg/op/mock/configuration.mock.go b/pkg/op/mock/configuration.mock.go index 137c09d5..0ef9d924 100644 --- a/pkg/op/mock/configuration.mock.go +++ b/pkg/op/mock/configuration.mock.go @@ -106,6 +106,20 @@ func (mr *MockConfigurationMockRecorder) BackChannelLogoutSupported() *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BackChannelLogoutSupported", reflect.TypeOf((*MockConfiguration)(nil).BackChannelLogoutSupported)) } +// CheckSessionIframe mocks base method. +func (m *MockConfiguration) CheckSessionIframe() *op.Endpoint { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CheckSessionIframe") + ret0, _ := ret[0].(*op.Endpoint) + return ret0 +} + +// CheckSessionIframe indicates an expected call of CheckSessionIframe. +func (mr *MockConfigurationMockRecorder) CheckSessionIframe() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckSessionIframe", reflect.TypeOf((*MockConfiguration)(nil).CheckSessionIframe)) +} + // CodeMethodS256Supported mocks base method. func (m *MockConfiguration) CodeMethodS256Supported() bool { m.ctrl.T.Helper() diff --git a/pkg/op/op.go b/pkg/op/op.go index 190c2c4f..58ae8386 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -339,6 +339,10 @@ func (o *Provider) DeviceAuthorizationEndpoint() *Endpoint { return o.endpoints.DeviceAuthorization } +func (o *Provider) CheckSessionIframe() *Endpoint { + return o.endpoints.CheckSessionIframe +} + func (o *Provider) KeysEndpoint() *Endpoint { return o.endpoints.JwksURI }