From 9d5d1cf3ea6226d78090aea07d03affbca6fc2f2 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 28 Dec 2023 04:25:18 -0500 Subject: [PATCH] feat: allow glob redirects (#7091) fixes #5110 --- .../api/grpc/oidc/v2/oidc_integration_test.go | 4 +- internal/api/oidc/client_converter.go | 14 ++++++ internal/api/oidc/client_integration_test.go | 4 +- internal/api/oidc/oidc_integration_test.go | 50 ++++++++++++++++++- internal/integration/oidc.go | 14 +++--- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/internal/api/grpc/oidc/v2/oidc_integration_test.go b/internal/api/grpc/oidc/v2/oidc_integration_test.go index db11f9022b..f3e7e0a75d 100644 --- a/internal/api/grpc/oidc/v2/oidc_integration_test.go +++ b/internal/api/grpc/oidc/v2/oidc_integration_test.go @@ -52,7 +52,7 @@ func TestMain(m *testing.M) { func TestServer_GetAuthRequest(t *testing.T) { project, err := Tester.CreateProject(CTX) require.NoError(t, err) - client, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) + client, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false) require.NoError(t, err) authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) @@ -96,7 +96,7 @@ func TestServer_GetAuthRequest(t *testing.T) { func TestServer_CreateCallback(t *testing.T) { project, err := Tester.CreateProject(CTX) require.NoError(t, err) - client, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) + client, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false) require.NoError(t, err) sessionResp, err := Tester.Client.SessionV2.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ diff --git a/internal/api/oidc/client_converter.go b/internal/api/oidc/client_converter.go index e6085926ae..b8334b578e 100644 --- a/internal/api/oidc/client_converter.go +++ b/internal/api/oidc/client_converter.go @@ -136,6 +136,20 @@ func (c *Client) IDTokenUserinfoClaimsAssertion() bool { return c.client.IDTokenUserinfoAssertion } +func (c *Client) RedirectURIGlobs() []string { + if c.DevMode() { + return c.RedirectURIs() + } + return nil +} + +func (c *Client) PostLogoutRedirectURIGlobs() []string { + if c.DevMode() { + return c.PostLogoutRedirectURIs() + } + return nil +} + func accessTokenTypeToOIDC(tokenType domain.OIDCTokenType) op.AccessTokenType { switch tokenType { case domain.OIDCTokenTypeBearer: diff --git a/internal/api/oidc/client_integration_test.go b/internal/api/oidc/client_integration_test.go index cd31607b3d..1593fbb8e9 100644 --- a/internal/api/oidc/client_integration_test.go +++ b/internal/api/oidc/client_integration_test.go @@ -59,7 +59,7 @@ func TestOPStorage_SetUserinfoFromToken(t *testing.T) { func TestServer_Introspect(t *testing.T) { project, err := Tester.CreateProject(CTX) require.NoError(t, err) - app, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) + app, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false) require.NoError(t, err) api, err := Tester.CreateAPIClient(CTX, project.GetId()) require.NoError(t, err) @@ -158,7 +158,7 @@ func TestServer_VerifyClient(t *testing.T) { inactiveClient, err := Tester.CreateOIDCInactivateClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) require.NoError(t, err) - nativeClient, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) + nativeClient, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false) require.NoError(t, err) basicWebClient, err := Tester.CreateOIDCWebClientBasic(CTX, redirectURI, logoutRedirectURI, project.GetId()) require.NoError(t, err) diff --git a/internal/api/oidc/oidc_integration_test.go b/internal/api/oidc/oidc_integration_test.go index 0e4b6e9c9d..f956b5c48f 100644 --- a/internal/api/oidc/oidc_integration_test.go +++ b/internal/api/oidc/oidc_integration_test.go @@ -174,6 +174,40 @@ func Test_ZITADEL_API_success(t *testing.T) { require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId()) } +func Test_ZITADEL_API_glob_redirects(t *testing.T) { + const redirectURI = "https://my-org-1yfnjl2xj-my-app.vercel.app/api/auth/callback/zitadel" + clientID := createClientWithOpts(t, clientOpts{ + redirectURI: "https://my-org-*-my-app.vercel.app/api/auth/callback/zitadel", + logoutURI: "https://my-org-*-my-app.vercel.app/", + devMode: true, + }) + authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, zitadelAudienceScope) + sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) + linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{ + AuthRequestId: authRequestID, + CallbackKind: &oidc_pb.CreateCallbackRequest_Session{ + Session: &oidc_pb.Session{ + SessionId: sessionID, + SessionToken: sessionToken, + }, + }, + }) + require.NoError(t, err) + + // code exchange + code := assertCodeResponse(t, linkResp.GetCallbackUrl()) + tokens, err := exchangeTokens(t, clientID, code) + require.NoError(t, err) + assertTokens(t, tokens, false) + assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) + + ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken)) + + myUserResp, err := Tester.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{}) + require.NoError(t, err) + require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId()) +} + func Test_ZITADEL_API_inactive_access_token(t *testing.T) { clientID := createClient(t) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope) @@ -257,9 +291,23 @@ func Test_ZITADEL_API_terminated_session(t *testing.T) { } func createClient(t testing.TB) string { + return createClientWithOpts(t, clientOpts{ + redirectURI: redirectURI, + logoutURI: logoutRedirectURI, + devMode: false, + }) +} + +type clientOpts struct { + redirectURI string + logoutURI string + devMode bool +} + +func createClientWithOpts(t testing.TB, opts clientOpts) string { project, err := Tester.CreateProject(CTX) require.NoError(t, err) - app, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) + app, err := Tester.CreateOIDCNativeClient(CTX, opts.redirectURI, opts.logoutURI, project.GetId(), opts.devMode) require.NoError(t, err) return app.GetClientId() } diff --git a/internal/integration/oidc.go b/internal/integration/oidc.go index aaf3ff1e31..e49b4e9c7e 100644 --- a/internal/integration/oidc.go +++ b/internal/integration/oidc.go @@ -23,7 +23,7 @@ import ( "github.com/zitadel/zitadel/pkg/grpc/user" ) -func (s *Tester) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, appType app.OIDCAppType, authMethod app.OIDCAuthMethodType) (*management.AddOIDCAppResponse, error) { +func (s *Tester) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, appType app.OIDCAppType, authMethod app.OIDCAuthMethodType, devMode bool) (*management.AddOIDCAppResponse, error) { return s.Client.Mgmt.AddOIDCApp(ctx, &management.AddOIDCAppRequest{ ProjectId: projectID, Name: fmt.Sprintf("app-%d", time.Now().UnixNano()), @@ -34,7 +34,7 @@ func (s *Tester) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedire AuthMethodType: authMethod, PostLogoutRedirectUris: []string{logoutRedirectURI}, Version: app.OIDCVersion_OIDC_VERSION_1_0, - DevMode: false, + DevMode: devMode, AccessTokenType: app.OIDCTokenType_OIDC_TOKEN_TYPE_JWT, AccessTokenRoleAssertion: false, IdTokenRoleAssertion: false, @@ -45,16 +45,16 @@ func (s *Tester) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedire }) } -func (s *Tester) CreateOIDCNativeClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) { - return s.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_NATIVE, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_NONE) +func (s *Tester) CreateOIDCNativeClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, devMode bool) (*management.AddOIDCAppResponse, error) { + return s.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_NATIVE, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_NONE, devMode) } func (s *Tester) CreateOIDCWebClientBasic(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) { - return s.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_WEB, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_BASIC) + return s.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_WEB, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_BASIC, false) } func (s *Tester) CreateOIDCWebClientJWT(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (client *management.AddOIDCAppResponse, keyData []byte, err error) { - client, err = s.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_WEB, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT) + client, err = s.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_WEB, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, false) if err != nil { return nil, nil, err } @@ -71,7 +71,7 @@ func (s *Tester) CreateOIDCWebClientJWT(ctx context.Context, redirectURI, logout } func (s *Tester) CreateOIDCInactivateClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) { - client, err := s.CreateOIDCNativeClient(ctx, redirectURI, logoutRedirectURI, projectID) + client, err := s.CreateOIDCNativeClient(ctx, redirectURI, logoutRedirectURI, projectID, false) if err != nil { return nil, err }