2023-07-14 11:16:16 +00:00
|
|
|
//go:build integration
|
|
|
|
|
|
|
|
package oidc_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2024-10-17 21:20:57 +00:00
|
|
|
"github.com/brianvoe/gofakeit/v6"
|
2023-07-14 11:16:16 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2023-10-17 15:19:51 +00:00
|
|
|
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
2023-07-14 11:16:16 +00:00
|
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
|
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
|
|
"github.com/zitadel/zitadel/internal/integration"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/auth"
|
2024-05-28 08:59:49 +00:00
|
|
|
mgmt "github.com/zitadel/zitadel/pkg/grpc/management"
|
2024-07-26 20:39:55 +00:00
|
|
|
oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
2023-07-14 11:16:16 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
CTX context.Context
|
|
|
|
CTXLOGIN context.Context
|
2024-05-28 08:59:49 +00:00
|
|
|
CTXIAM context.Context
|
2024-09-06 12:47:57 +00:00
|
|
|
Instance *integration.Instance
|
2023-07-14 11:16:16 +00:00
|
|
|
User *user.AddHumanUserResponse
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2023-12-05 17:01:03 +00:00
|
|
|
redirectURI = "https://callback"
|
2023-07-14 11:16:16 +00:00
|
|
|
redirectURIImplicit = "http://localhost:9999/callback"
|
2023-12-05 17:01:03 +00:00
|
|
|
logoutRedirectURI = "https://logged-out"
|
2023-07-14 11:16:16 +00:00
|
|
|
zitadelAudienceScope = domain.ProjectIDScope + domain.ProjectIDScopeZITADEL + domain.AudSuffix
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
os.Exit(func() int {
|
2024-09-06 12:47:57 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
|
2023-07-14 11:16:16 +00:00
|
|
|
defer cancel()
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
Instance = integration.NewInstance(ctx)
|
2023-07-14 11:16:16 +00:00
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
CTX = Instance.WithAuthorization(ctx, integration.UserTypeOrgOwner)
|
|
|
|
User = Instance.CreateHumanUser(CTX)
|
|
|
|
Instance.SetUserPassword(CTX, User.GetUserId(), integration.UserPassword, false)
|
|
|
|
Instance.RegisterUserPasskey(CTX, User.GetUserId())
|
|
|
|
CTXLOGIN = Instance.WithAuthorization(ctx, integration.UserTypeLogin)
|
|
|
|
CTXIAM = Instance.WithAuthorization(ctx, integration.UserTypeIAMOwner)
|
2023-07-14 11:16:16 +00:00
|
|
|
return m.Run()
|
|
|
|
}())
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_ZITADEL_API_missing_audience_scope(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, false)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-07-14 11:16:16 +00:00
|
|
|
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-14 11:16:16 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, myUserResp)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_ZITADEL_API_missing_authentication(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, zitadelAudienceScope)
|
|
|
|
createResp, err := Instance.Client.SessionV2.CreateSession(CTX, &session.CreateSessionRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
Checks: &session.Checks{
|
|
|
|
User: &session.CheckUser{
|
|
|
|
Search: &session.CheckUser_UserId{UserId: User.GetUserId()},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
2024-09-06 12:47:57 +00:00
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
AuthRequestId: authRequestID,
|
|
|
|
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
|
|
|
Session: &oidc_pb.Session{
|
|
|
|
SessionId: createResp.GetSessionId(),
|
|
|
|
SessionToken: createResp.GetSessionToken(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// code exchange
|
|
|
|
code := assertCodeResponse(t, linkResp.GetCallbackUrl())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-14 11:16:16 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, myUserResp)
|
|
|
|
}
|
|
|
|
|
2024-05-28 08:59:49 +00:00
|
|
|
func Test_ZITADEL_API_missing_mfa_policy(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
2024-10-17 21:20:57 +00:00
|
|
|
org := Instance.CreateOrganization(CTXIAM, fmt.Sprintf("ZITADEL_API_MISSING_MFA_%s", gofakeit.AppName()), gofakeit.Email())
|
2024-05-28 08:59:49 +00:00
|
|
|
userID := org.CreatedAdmins[0].GetUserId()
|
2024-09-06 12:47:57 +00:00
|
|
|
Instance.SetUserPassword(CTXIAM, userID, integration.UserPassword, false)
|
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, zitadelAudienceScope)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreatePasswordSession(t, CTXLOGIN, userID, integration.UserPassword)
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2024-05-28 08:59:49 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2024-05-28 08:59:49 +00:00
|
|
|
require.NoError(t, err)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, userID, armPassword, startTime, changeTime, sessionID)
|
2024-05-28 08:59:49 +00:00
|
|
|
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
|
|
|
|
|
|
|
// pre check if request would succeed
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2024-05-28 08:59:49 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, userID, myUserResp.GetUser().GetId())
|
|
|
|
|
|
|
|
// require MFA
|
|
|
|
ctxOrg := metadata.AppendToOutgoingContext(CTXIAM, "x-zitadel-orgid", org.GetOrganizationId())
|
2024-09-06 12:47:57 +00:00
|
|
|
_, err = Instance.Client.Mgmt.AddCustomLoginPolicy(ctxOrg, &mgmt.AddCustomLoginPolicyRequest{
|
2024-05-28 08:59:49 +00:00
|
|
|
ForceMfa: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// make sure policy is projected
|
|
|
|
retryDuration := 5 * time.Second
|
|
|
|
if ctxDeadline, ok := CTX.Deadline(); ok {
|
|
|
|
retryDuration = time.Until(ctxDeadline)
|
|
|
|
}
|
|
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
2024-09-06 12:47:57 +00:00
|
|
|
got, getErr := Instance.Client.Mgmt.GetLoginPolicy(ctxOrg, &mgmt.GetLoginPolicyRequest{})
|
2024-05-28 08:59:49 +00:00
|
|
|
assert.NoError(ttt, getErr)
|
|
|
|
assert.False(ttt, got.GetPolicy().IsDefault)
|
|
|
|
|
|
|
|
}, retryDuration, time.Millisecond*100, "timeout waiting for login policy")
|
|
|
|
|
|
|
|
// now it must fail
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err = Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2024-05-28 08:59:49 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, myUserResp)
|
|
|
|
}
|
|
|
|
|
2023-07-14 11:16:16 +00:00
|
|
|
func Test_ZITADEL_API_success(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, zitadelAudienceScope)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, false)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-07-14 11:16:16 +00:00
|
|
|
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId())
|
|
|
|
}
|
|
|
|
|
2023-12-28 09:25:18 +00:00
|
|
|
func Test_ZITADEL_API_glob_redirects(t *testing.T) {
|
|
|
|
const redirectURI = "https://my-org-1yfnjl2xj-my-app.vercel.app/api/auth/callback/zitadel"
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClientWithOpts(t, Instance, clientOpts{
|
2023-12-28 09:25:18 +00:00
|
|
|
redirectURI: "https://my-org-*-my-app.vercel.app/api/auth/callback/zitadel",
|
|
|
|
logoutURI: "https://my-org-*-my-app.vercel.app/",
|
|
|
|
devMode: true,
|
|
|
|
})
|
2024-09-06 12:47:57 +00:00
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, zitadelAudienceScope)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2023-12-28 09:25:18 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2023-12-28 09:25:18 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, false)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-12-28 09:25:18 +00:00
|
|
|
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-12-28 09:25:18 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId())
|
|
|
|
}
|
|
|
|
|
2023-07-14 11:16:16 +00:00
|
|
|
func Test_ZITADEL_API_inactive_access_token(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, true)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-07-14 11:16:16 +00:00
|
|
|
|
|
|
|
// make sure token works
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId())
|
|
|
|
|
|
|
|
// refresh token
|
|
|
|
newTokens, err := refreshTokens(t, clientID, tokens.RefreshToken)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEmpty(t, newTokens.AccessToken)
|
|
|
|
|
|
|
|
// use invalidated token
|
|
|
|
ctx = metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err = Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-14 11:16:16 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, myUserResp)
|
|
|
|
}
|
|
|
|
|
2023-07-19 11:17:39 +00:00
|
|
|
func Test_ZITADEL_API_terminated_session(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
|
|
|
provider, err := Instance.CreateRelyingParty(CTX, clientID, redirectURI)
|
2023-07-19 11:17:39 +00:00
|
|
|
require.NoError(t, err)
|
2024-09-06 12:47:57 +00:00
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2023-07-19 11:17:39 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2023-07-19 11:17:39 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, true)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-07-19 11:17:39 +00:00
|
|
|
|
|
|
|
// make sure token works
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-19 11:17:39 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId())
|
|
|
|
|
2024-02-28 09:30:05 +00:00
|
|
|
// end session
|
2023-10-17 15:19:51 +00:00
|
|
|
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state")
|
2023-07-19 11:17:39 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String())
|
|
|
|
|
|
|
|
// use token from terminated session
|
|
|
|
ctx = metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err = Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2023-07-19 11:17:39 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, myUserResp)
|
|
|
|
}
|
|
|
|
|
2024-02-28 09:30:05 +00:00
|
|
|
func Test_ZITADEL_API_terminated_session_user_disabled(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
clientID, _ := createClient(t, Instance)
|
2024-02-28 09:30:05 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
disable func(userID string) error
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "deactivated",
|
|
|
|
disable: func(userID string) error {
|
2024-09-06 12:47:57 +00:00
|
|
|
_, err := Instance.Client.UserV2.DeactivateUser(CTX, &user.DeactivateUserRequest{UserId: userID})
|
2024-02-28 09:30:05 +00:00
|
|
|
return err
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "locked",
|
|
|
|
disable: func(userID string) error {
|
2024-09-06 12:47:57 +00:00
|
|
|
_, err := Instance.Client.UserV2.LockUser(CTX, &user.LockUserRequest{UserId: userID})
|
2024-02-28 09:30:05 +00:00
|
|
|
return err
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "deleted",
|
|
|
|
disable: func(userID string) error {
|
2024-09-06 12:47:57 +00:00
|
|
|
_, err := Instance.Client.UserV2.DeleteUser(CTX, &user.DeleteUserRequest{UserId: userID})
|
2024-02-28 09:30:05 +00:00
|
|
|
return err
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2024-09-06 12:47:57 +00:00
|
|
|
disabledUser := Instance.CreateHumanUser(CTX)
|
|
|
|
Instance.SetUserPassword(CTX, disabledUser.GetUserId(), integration.UserPassword, false)
|
|
|
|
authRequestID := createAuthRequest(t, Instance, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Instance.CreatePasswordSession(t, CTXLOGIN, disabledUser.GetUserId(), integration.UserPassword)
|
|
|
|
linkResp, err := Instance.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
2024-02-28 09:30:05 +00:00
|
|
|
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())
|
2024-09-06 12:47:57 +00:00
|
|
|
tokens, err := exchangeTokens(t, Instance, clientID, code, redirectURI)
|
2024-02-28 09:30:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, true)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, disabledUser.GetUserId(), armPassword, startTime, changeTime, sessionID)
|
2024-02-28 09:30:05 +00:00
|
|
|
|
|
|
|
// make sure token works
|
|
|
|
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err := Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2024-02-28 09:30:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, disabledUser.GetUserId(), myUserResp.GetUser().GetId())
|
|
|
|
|
|
|
|
// deactivate user
|
|
|
|
err = tt.disable(disabledUser.GetUserId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// use token from deactivated user
|
|
|
|
ctx = metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken))
|
2024-09-06 12:47:57 +00:00
|
|
|
myUserResp, err = Instance.Client.Auth.GetMyUser(ctx, &auth.GetMyUserRequest{})
|
2024-02-28 09:30:05 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Nil(t, myUserResp)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func createClient(t testing.TB, instance *integration.Instance) (clientID, projectID string) {
|
|
|
|
return createClientWithOpts(t, instance, clientOpts{
|
2023-12-28 09:25:18 +00:00
|
|
|
redirectURI: redirectURI,
|
|
|
|
logoutURI: logoutRedirectURI,
|
|
|
|
devMode: false,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type clientOpts struct {
|
|
|
|
redirectURI string
|
|
|
|
logoutURI string
|
|
|
|
devMode bool
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func createClientWithOpts(t testing.TB, instance *integration.Instance, opts clientOpts) (clientID, projectID string) {
|
|
|
|
ctx := instance.WithAuthorization(CTX, integration.UserTypeOrgOwner)
|
|
|
|
|
|
|
|
project, err := instance.CreateProject(ctx)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
2024-09-06 12:47:57 +00:00
|
|
|
app, err := instance.CreateOIDCNativeClient(ctx, opts.redirectURI, opts.logoutURI, project.GetId(), opts.devMode)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
2024-04-09 13:15:35 +00:00
|
|
|
return app.GetClientId(), project.GetId()
|
2023-07-14 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func createImplicitClient(t testing.TB) string {
|
2024-09-06 12:47:57 +00:00
|
|
|
app, err := Instance.CreateOIDCImplicitFlowClient(CTX, redirectURIImplicit)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return app.GetClientId()
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func createAuthRequest(t testing.TB, instance *integration.Instance, clientID, redirectURI string, scope ...string) string {
|
|
|
|
redURL, err := instance.CreateOIDCAuthRequest(CTX, clientID, instance.Users.Get(integration.UserTypeLogin).ID, redirectURI, scope...)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return redURL
|
|
|
|
}
|
|
|
|
|
|
|
|
func createAuthRequestImplicit(t testing.TB, clientID, redirectURI string, scope ...string) string {
|
2024-09-06 12:47:57 +00:00
|
|
|
redURL, err := Instance.CreateOIDCAuthRequestImplicit(CTX, clientID, Instance.Users.Get(integration.UserTypeLogin).ID, redirectURI, scope...)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return redURL
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertOIDCTime(t *testing.T, actual oidc.Time, expected time.Time) {
|
|
|
|
assertOIDCTimeRange(t, actual, expected, expected)
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertOIDCTimeRange(t *testing.T, actual oidc.Time, expectedStart, expectedEnd time.Time) {
|
2024-09-06 12:47:57 +00:00
|
|
|
assert.WithinRange(t, actual.AsTime(), expectedStart.Add(-10*time.Second), expectedEnd.Add(10*time.Second))
|
2023-07-14 11:16:16 +00:00
|
|
|
}
|