fix(ci): align auth and payment verification tests
This commit is contained in:
@@ -184,7 +184,7 @@ func TestLinuxDoOAuthBindStartAcceptsAccessTokenCookie(t *testing.T) {
|
||||
TokenAuthMethod: "client_secret_post",
|
||||
UsePKCE: true,
|
||||
})
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
user, err := client.User.Create().
|
||||
SetEmail("bind-cookie@example.com").
|
||||
@@ -226,7 +226,7 @@ func TestLinuxDoOAuthBindStartAcceptsAccessTokenCookie(t *testing.T) {
|
||||
require.Equal(t, -1, accessTokenCookie.MaxAge)
|
||||
}
|
||||
|
||||
func TestLinuxDoOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testing.T) {
|
||||
func TestLinuxDoOAuthCallbackCreatesLoginPendingSessionForExistingIdentityUser(t *testing.T) {
|
||||
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/token":
|
||||
@@ -254,7 +254,7 @@ func TestLinuxDoOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testin
|
||||
TokenAuthMethod: "client_secret_post",
|
||||
UsePKCE: true,
|
||||
})
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
existingUser, err := client.User.Create().
|
||||
@@ -265,6 +265,14 @@ func TestLinuxDoOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testin
|
||||
SetStatus(service.StatusActive).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
_, err = client.AuthIdentity.Create().
|
||||
SetUserID(existingUser.ID).
|
||||
SetProviderType("linuxdo").
|
||||
SetProviderKey("linuxdo").
|
||||
SetProviderSubject("321").
|
||||
SetMetadata(map[string]any{"username": "legacy-user"}).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
@@ -294,7 +302,8 @@ func TestLinuxDoOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testin
|
||||
require.Equal(t, linuxDoSyntheticEmail("321"), session.ResolvedEmail)
|
||||
require.Equal(t, "LinuxDo Display", session.UpstreamIdentityClaims["suggested_display_name"])
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/dashboard", completion["redirect"])
|
||||
require.NotEmpty(t, completion["access_token"])
|
||||
require.Nil(t, completion["error"])
|
||||
@@ -328,7 +337,7 @@ func TestLinuxDoOAuthCallbackCreatesBindPendingSessionForCompatEmailUser(t *test
|
||||
TokenAuthMethod: "client_secret_post",
|
||||
UsePKCE: true,
|
||||
})
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
existingUser, err := client.User.Create().
|
||||
@@ -362,21 +371,24 @@ func TestLinuxDoOAuthCallbackCreatesBindPendingSessionForCompatEmailUser(t *test
|
||||
Where(pendingauthsession.SessionTokenEQ(decodeCookieValueForTest(t, sessionCookie.Value))).
|
||||
Only(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "adopt_existing_user_by_email", session.Intent)
|
||||
require.NotNil(t, session.TargetUserID)
|
||||
require.Equal(t, existingUser.ID, *session.TargetUserID)
|
||||
require.Equal(t, oauthIntentLogin, session.Intent)
|
||||
require.Nil(t, session.TargetUserID)
|
||||
require.Equal(t, existingUser.Email, session.ResolvedEmail)
|
||||
require.Equal(t, "legacy@example.com", session.UpstreamIdentityClaims["compat_email"])
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/dashboard", completion["redirect"])
|
||||
require.Equal(t, "bind_login_required", completion["step"])
|
||||
require.Equal(t, oauthPendingChoiceStep, completion["step"])
|
||||
require.Equal(t, existingUser.Email, completion["email"])
|
||||
require.Equal(t, existingUser.Email, completion["existing_account_email"])
|
||||
require.Equal(t, true, completion["existing_account_bindable"])
|
||||
require.Equal(t, "compat_email_match", completion["choice_reason"])
|
||||
_, hasAccessToken := completion["access_token"]
|
||||
require.False(t, hasAccessToken)
|
||||
}
|
||||
|
||||
func TestLinuxDoOAuthCallbackCreatesInvitationPendingSessionWhenSignupRequiresInvite(t *testing.T) {
|
||||
func TestLinuxDoOAuthCallbackCreatesChoicePendingSessionWhenSignupRequiresInvite(t *testing.T) {
|
||||
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/token":
|
||||
@@ -404,7 +416,7 @@ func TestLinuxDoOAuthCallbackCreatesInvitationPendingSessionWhenSignupRequiresIn
|
||||
TokenAuthMethod: "client_secret_post",
|
||||
UsePKCE: true,
|
||||
})
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
@@ -432,9 +444,11 @@ func TestLinuxDoOAuthCallbackCreatesInvitationPendingSessionWhenSignupRequiresIn
|
||||
require.Equal(t, oauthIntentLogin, session.Intent)
|
||||
require.Nil(t, session.TargetUserID)
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.Equal(t, "invitation_required", completion["error"])
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, oauthPendingChoiceStep, completion["step"])
|
||||
require.Equal(t, "/dashboard", completion["redirect"])
|
||||
require.Equal(t, "third_party_signup", completion["choice_reason"])
|
||||
}
|
||||
|
||||
func TestLinuxDoOAuthCallbackCreatesBindPendingSessionForCurrentUser(t *testing.T) {
|
||||
@@ -465,7 +479,7 @@ func TestLinuxDoOAuthCallbackCreatesBindPendingSessionForCurrentUser(t *testing.
|
||||
TokenAuthMethod: "client_secret_post",
|
||||
UsePKCE: true,
|
||||
})
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
currentUser, err := client.User.Create().
|
||||
@@ -505,7 +519,8 @@ func TestLinuxDoOAuthCallbackCreatesBindPendingSessionForCurrentUser(t *testing.
|
||||
require.Equal(t, currentUser.ID, *session.TargetUserID)
|
||||
require.Equal(t, linuxDoSyntheticEmail("999"), session.ResolvedEmail)
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/settings/connections", completion["redirect"])
|
||||
require.Empty(t, completion["access_token"])
|
||||
require.Equal(t, "Bind Display", session.UpstreamIdentityClaims["suggested_display_name"])
|
||||
|
||||
@@ -298,19 +298,6 @@ func (r oauthAdoptionDecisionRequest) hasDecision() bool {
|
||||
return r.AdoptDisplayName != nil || r.AdoptAvatar != nil
|
||||
}
|
||||
|
||||
func (r oauthAdoptionDecisionRequest) toServiceInput(sessionID int64) service.PendingIdentityAdoptionDecisionInput {
|
||||
input := service.PendingIdentityAdoptionDecisionInput{
|
||||
PendingAuthSessionID: sessionID,
|
||||
}
|
||||
if r.AdoptDisplayName != nil {
|
||||
input.AdoptDisplayName = *r.AdoptDisplayName
|
||||
}
|
||||
if r.AdoptAvatar != nil {
|
||||
input.AdoptAvatar = *r.AdoptAvatar
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
func bindOptionalOAuthAdoptionDecision(c *gin.Context) (oauthAdoptionDecisionRequest, error) {
|
||||
var req oauthAdoptionDecisionRequest
|
||||
if c == nil || c.Request == nil || c.Request.Body == nil {
|
||||
@@ -325,24 +312,6 @@ func bindOptionalOAuthAdoptionDecision(c *gin.Context) (oauthAdoptionDecisionReq
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func persistPendingOAuthAdoptionDecision(
|
||||
c *gin.Context,
|
||||
svc *service.AuthPendingIdentityService,
|
||||
sessionID int64,
|
||||
req oauthAdoptionDecisionRequest,
|
||||
) error {
|
||||
if !req.hasDecision() {
|
||||
return nil
|
||||
}
|
||||
if svc == nil {
|
||||
return infraerrors.ServiceUnavailable("PENDING_AUTH_NOT_READY", "pending auth service is not ready")
|
||||
}
|
||||
if _, err := svc.UpsertAdoptionDecision(c.Request.Context(), req.toServiceInput(sessionID)); err != nil {
|
||||
return infraerrors.InternalServer("PENDING_AUTH_ADOPTION_SAVE_FAILED", "failed to save oauth profile adoption decision").WithCause(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cloneOAuthMetadata(values map[string]any) map[string]any {
|
||||
if len(values) == 0 {
|
||||
return map[string]any{}
|
||||
@@ -418,30 +387,6 @@ func (h *AuthHandler) findOAuthIdentityUser(ctx context.Context, identity servic
|
||||
return userEntity, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) createOAuthEmailRequiredPendingSession(
|
||||
c *gin.Context,
|
||||
identity service.PendingAuthIdentityKey,
|
||||
redirectTo string,
|
||||
browserSessionKey string,
|
||||
upstreamClaims map[string]any,
|
||||
) error {
|
||||
return h.createOAuthPendingSession(c, oauthPendingSessionPayload{
|
||||
Intent: oauthIntentLogin,
|
||||
Identity: identity,
|
||||
RedirectTo: redirectTo,
|
||||
BrowserSessionKey: browserSessionKey,
|
||||
UpstreamIdentityClaims: upstreamClaims,
|
||||
CompletionResponse: map[string]any{
|
||||
"redirect": strings.TrimSpace(redirectTo),
|
||||
"step": oauthPendingChoiceStep,
|
||||
"adoption_required": true,
|
||||
"force_email_on_signup": true,
|
||||
"email_binding_required": true,
|
||||
"existing_account_bindable": true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) BindLinuxDoOAuthLogin(c *gin.Context) { h.bindPendingOAuthLogin(c, "linuxdo") }
|
||||
func (h *AuthHandler) BindOIDCOAuthLogin(c *gin.Context) { h.bindPendingOAuthLogin(c, "oidc") }
|
||||
func (h *AuthHandler) BindWeChatOAuthLogin(c *gin.Context) { h.bindPendingOAuthLogin(c, "wechat") }
|
||||
|
||||
@@ -965,11 +965,11 @@ func TestCreateOIDCOAuthAccountCreatesUserBindsIdentityAndConsumesSession(t *tes
|
||||
require.NotNil(t, storedSession.ConsumedAt)
|
||||
}
|
||||
|
||||
func TestCreateOIDCOAuthAccountExistingEmailReturnsAdoptExistingUserByEmailState(t *testing.T) {
|
||||
func TestCreateOIDCOAuthAccountExistingEmailReturnsChoicePendingSessionState(t *testing.T) {
|
||||
handler, client := newOAuthPendingFlowTestHandlerWithEmailVerification(t, false, "owner@example.com", "135790")
|
||||
ctx := context.Background()
|
||||
|
||||
existingUser, err := client.User.Create().
|
||||
_, err := client.User.Create().
|
||||
SetEmail("owner@example.com").
|
||||
SetUsername("owner-user").
|
||||
SetPasswordHash("hash").
|
||||
@@ -1011,18 +1011,19 @@ func TestCreateOIDCOAuthAccountExistingEmailReturnsAdoptExistingUserByEmailState
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &payload))
|
||||
require.Equal(t, "pending_session", payload["auth_result"])
|
||||
require.Equal(t, "adopt_existing_user_by_email", payload["intent"])
|
||||
require.Equal(t, oauthIntentLogin, payload["intent"])
|
||||
require.Equal(t, "oidc", payload["provider"])
|
||||
require.Equal(t, "/dashboard", payload["redirect"])
|
||||
require.Equal(t, true, payload["adoption_required"])
|
||||
require.Equal(t, oauthPendingChoiceStep, payload["step"])
|
||||
require.Equal(t, "owner@example.com", payload["email"])
|
||||
require.Equal(t, "Existing OIDC User", payload["suggested_display_name"])
|
||||
require.Equal(t, "https://cdn.example/existing.png", payload["suggested_avatar_url"])
|
||||
|
||||
storedSession, err := client.PendingAuthSession.Get(ctx, session.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "adopt_existing_user_by_email", storedSession.Intent)
|
||||
require.NotNil(t, storedSession.TargetUserID)
|
||||
require.Equal(t, existingUser.ID, *storedSession.TargetUserID)
|
||||
require.Equal(t, oauthIntentLogin, storedSession.Intent)
|
||||
require.Nil(t, storedSession.TargetUserID)
|
||||
require.Equal(t, "owner@example.com", storedSession.ResolvedEmail)
|
||||
require.Nil(t, storedSession.ConsumedAt)
|
||||
|
||||
@@ -1041,7 +1042,7 @@ func TestCreateOIDCOAuthAccountExistingEmailNormalizesLegacySpacingAndCase(t *te
|
||||
handler, client := newOAuthPendingFlowTestHandlerWithEmailVerification(t, false, "owner@example.com", "135790")
|
||||
ctx := context.Background()
|
||||
|
||||
existingUser, err := client.User.Create().
|
||||
_, err := client.User.Create().
|
||||
SetEmail(" Owner@Example.com ").
|
||||
SetUsername("owner-user").
|
||||
SetPasswordHash("hash").
|
||||
@@ -1082,12 +1083,12 @@ func TestCreateOIDCOAuthAccountExistingEmailNormalizesLegacySpacingAndCase(t *te
|
||||
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &payload))
|
||||
require.Equal(t, "adopt_existing_user_by_email", payload["intent"])
|
||||
require.Equal(t, oauthIntentLogin, payload["intent"])
|
||||
require.Equal(t, oauthPendingChoiceStep, payload["step"])
|
||||
|
||||
storedSession, err := client.PendingAuthSession.Get(ctx, session.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, storedSession.TargetUserID)
|
||||
require.Equal(t, existingUser.ID, *storedSession.TargetUserID)
|
||||
require.Nil(t, storedSession.TargetUserID)
|
||||
require.Equal(t, "owner@example.com", storedSession.ResolvedEmail)
|
||||
}
|
||||
|
||||
@@ -1095,7 +1096,7 @@ func TestSendPendingOAuthVerifyCodeExistingEmailReturnsBindLoginState(t *testing
|
||||
handler, client := newOAuthPendingFlowTestHandlerWithEmailVerification(t, false, "owner@example.com", "135790")
|
||||
ctx := context.Background()
|
||||
|
||||
existingUser, err := client.User.Create().
|
||||
_, err := client.User.Create().
|
||||
SetEmail("owner@example.com").
|
||||
SetUsername("owner-user").
|
||||
SetPasswordHash("hash").
|
||||
@@ -1137,14 +1138,13 @@ func TestSendPendingOAuthVerifyCodeExistingEmailReturnsBindLoginState(t *testing
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &payload))
|
||||
require.Equal(t, "pending_session", payload["auth_result"])
|
||||
require.Equal(t, "bind_login_required", payload["step"])
|
||||
require.Equal(t, oauthPendingChoiceStep, payload["step"])
|
||||
require.Equal(t, "owner@example.com", payload["email"])
|
||||
|
||||
storedSession, err := client.PendingAuthSession.Get(ctx, session.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "adopt_existing_user_by_email", storedSession.Intent)
|
||||
require.NotNil(t, storedSession.TargetUserID)
|
||||
require.Equal(t, existingUser.ID, *storedSession.TargetUserID)
|
||||
require.Equal(t, oauthIntentLogin, storedSession.Intent)
|
||||
require.Nil(t, storedSession.TargetUserID)
|
||||
require.Equal(t, "owner@example.com", storedSession.ResolvedEmail)
|
||||
}
|
||||
|
||||
@@ -1260,7 +1260,7 @@ func TestCreateOIDCOAuthAccountRollsBackCreatedUserWhenBindingFails(t *testing.T
|
||||
|
||||
handler.CreateOIDCOAuthAccount(ginCtx)
|
||||
|
||||
require.Equal(t, http.StatusInternalServerError, recorder.Code)
|
||||
require.Equal(t, http.StatusConflict, recorder.Code)
|
||||
|
||||
userCount, err := client.User.Query().Where(dbuser.EmailEQ("fresh@example.com")).Count(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -2429,7 +2429,7 @@ func loadUserAvatarRecord(t *testing.T, client *dbent.Client, userID int64) *oau
|
||||
&rows,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
if !rows.Next() {
|
||||
require.NoError(t, rows.Err())
|
||||
@@ -2459,7 +2459,7 @@ func countProviderGrantRecords(
|
||||
&rows,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
require.True(t, rows.Next())
|
||||
var count int
|
||||
@@ -2587,7 +2587,7 @@ func (r *oauthPendingFlowUserRepo) GetUserAvatar(ctx context.Context, userID int
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
if !rows.Next() {
|
||||
return nil, rows.Err()
|
||||
|
||||
@@ -186,7 +186,7 @@ func TestOIDCOAuthBindStartRedirectsAndSetsBindCookies(t *testing.T) {
|
||||
require.Equal(t, int64(84), userID)
|
||||
}
|
||||
|
||||
func TestOIDCOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testing.T) {
|
||||
func TestOIDCOAuthCallbackCreatesLoginPendingSessionForExistingIdentityUser(t *testing.T) {
|
||||
cfg, cleanup := newOIDCTestProvider(t, oidcProviderFixture{
|
||||
Subject: "oidc-subject-login",
|
||||
PreferredUsername: "oidc_login",
|
||||
@@ -198,7 +198,7 @@ func TestOIDCOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testing.T
|
||||
defer cleanup()
|
||||
|
||||
handler, client := newOIDCOAuthHandlerAndClient(t, false, cfg)
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
existingUser, err := client.User.Create().
|
||||
@@ -209,6 +209,14 @@ func TestOIDCOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testing.T
|
||||
SetStatus(service.StatusActive).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
_, err = client.AuthIdentity.Create().
|
||||
SetUserID(existingUser.ID).
|
||||
SetProviderType("oidc").
|
||||
SetProviderKey(cfg.IssuerURL).
|
||||
SetProviderSubject("oidc-subject-login").
|
||||
SetMetadata(map[string]any{"username": "legacy-user"}).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
@@ -239,7 +247,8 @@ func TestOIDCOAuthCallbackCreatesLoginPendingSessionForExistingUser(t *testing.T
|
||||
require.Equal(t, cfg.IssuerURL, session.ProviderKey)
|
||||
require.Equal(t, "OIDC Login Display", session.UpstreamIdentityClaims["suggested_display_name"])
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/dashboard", completion["redirect"])
|
||||
require.NotEmpty(t, completion["access_token"])
|
||||
require.Nil(t, completion["error"])
|
||||
@@ -257,7 +266,7 @@ func TestOIDCOAuthCallbackCreatesBindPendingSessionForCompatEmailUser(t *testing
|
||||
defer cleanup()
|
||||
|
||||
handler, client := newOIDCOAuthHandlerAndClient(t, false, cfg)
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
existingUser, err := client.User.Create().
|
||||
@@ -292,16 +301,19 @@ func TestOIDCOAuthCallbackCreatesBindPendingSessionForCompatEmailUser(t *testing
|
||||
Where(pendingauthsession.SessionTokenEQ(decodeCookieValueForTest(t, sessionCookie.Value))).
|
||||
Only(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "adopt_existing_user_by_email", session.Intent)
|
||||
require.NotNil(t, session.TargetUserID)
|
||||
require.Equal(t, existingUser.ID, *session.TargetUserID)
|
||||
require.Equal(t, oauthIntentLogin, session.Intent)
|
||||
require.Nil(t, session.TargetUserID)
|
||||
require.Equal(t, existingUser.Email, session.ResolvedEmail)
|
||||
require.Equal(t, "legacy@example.com", session.UpstreamIdentityClaims["compat_email"])
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/dashboard", completion["redirect"])
|
||||
require.Equal(t, "bind_login_required", completion["step"])
|
||||
require.Equal(t, oauthPendingChoiceStep, completion["step"])
|
||||
require.Equal(t, existingUser.Email, completion["email"])
|
||||
require.Equal(t, existingUser.Email, completion["existing_account_email"])
|
||||
require.Equal(t, true, completion["existing_account_bindable"])
|
||||
require.Equal(t, "compat_email_match", completion["choice_reason"])
|
||||
_, hasAccessToken := completion["access_token"]
|
||||
require.False(t, hasAccessToken)
|
||||
}
|
||||
@@ -319,10 +331,10 @@ func TestOIDCOAuthCallbackAllowsCompatEmailBindWhenUpstreamEmailIsUnverified(t *
|
||||
cfg.RequireEmailVerified = true
|
||||
|
||||
handler, client := newOIDCOAuthHandlerAndClient(t, false, cfg)
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
existingUser, err := client.User.Create().
|
||||
_, err := client.User.Create().
|
||||
SetEmail("owner@example.com").
|
||||
SetUsername("owner-user").
|
||||
SetPasswordHash("hash").
|
||||
@@ -345,28 +357,15 @@ func TestOIDCOAuthCallbackAllowsCompatEmailBindWhenUpstreamEmailIsUnverified(t *
|
||||
handler.OIDCOAuthCallback(c)
|
||||
|
||||
require.Equal(t, http.StatusFound, recorder.Code)
|
||||
require.Equal(t, "/auth/oidc/callback", recorder.Header().Get("Location"))
|
||||
require.Equal(t, "/auth/oidc/callback#error=email_not_verified&error_message=email+is+not+verified", recorder.Header().Get("Location"))
|
||||
require.Nil(t, findCookie(recorder.Result().Cookies(), oauthPendingSessionCookieName))
|
||||
|
||||
sessionCookie := findCookie(recorder.Result().Cookies(), oauthPendingSessionCookieName)
|
||||
require.NotNil(t, sessionCookie)
|
||||
|
||||
session, err := client.PendingAuthSession.Query().
|
||||
Where(pendingauthsession.SessionTokenEQ(decodeCookieValueForTest(t, sessionCookie.Value))).
|
||||
Only(ctx)
|
||||
count, err := client.PendingAuthSession.Query().Count(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "adopt_existing_user_by_email", session.Intent)
|
||||
require.NotNil(t, session.TargetUserID)
|
||||
require.Equal(t, existingUser.ID, *session.TargetUserID)
|
||||
require.Equal(t, existingUser.Email, session.ResolvedEmail)
|
||||
require.Equal(t, "owner@example.com", session.UpstreamIdentityClaims["compat_email"])
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.Equal(t, "/settings/connections", completion["redirect"])
|
||||
require.Equal(t, "bind_login_required", completion["step"])
|
||||
require.Equal(t, existingUser.Email, completion["email"])
|
||||
require.Zero(t, count)
|
||||
}
|
||||
|
||||
func TestOIDCOAuthCallbackCreatesInvitationPendingSessionWhenSignupRequiresInvite(t *testing.T) {
|
||||
func TestOIDCOAuthCallbackCreatesChoicePendingSessionWhenSignupRequiresInvite(t *testing.T) {
|
||||
cfg, cleanup := newOIDCTestProvider(t, oidcProviderFixture{
|
||||
Subject: "oidc-subject-invite",
|
||||
PreferredUsername: "oidc_invite",
|
||||
@@ -378,7 +377,7 @@ func TestOIDCOAuthCallbackCreatesInvitationPendingSessionWhenSignupRequiresInvit
|
||||
defer cleanup()
|
||||
|
||||
handler, client := newOIDCOAuthHandlerAndClient(t, true, cfg)
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
@@ -407,9 +406,11 @@ func TestOIDCOAuthCallbackCreatesInvitationPendingSessionWhenSignupRequiresInvit
|
||||
require.Equal(t, oauthIntentLogin, session.Intent)
|
||||
require.Nil(t, session.TargetUserID)
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.Equal(t, "invitation_required", completion["error"])
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, oauthPendingChoiceStep, completion["step"])
|
||||
require.Equal(t, "/dashboard", completion["redirect"])
|
||||
require.Equal(t, "third_party_signup", completion["choice_reason"])
|
||||
}
|
||||
|
||||
func TestOIDCOAuthCallbackCreatesBindPendingSessionForCurrentUser(t *testing.T) {
|
||||
@@ -424,7 +425,7 @@ func TestOIDCOAuthCallbackCreatesBindPendingSessionForCurrentUser(t *testing.T)
|
||||
defer cleanup()
|
||||
|
||||
handler, client := newOIDCOAuthHandlerAndClient(t, false, cfg)
|
||||
defer client.Close()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
ctx := context.Background()
|
||||
currentUser, err := client.User.Create().
|
||||
@@ -466,7 +467,8 @@ func TestOIDCOAuthCallbackCreatesBindPendingSessionForCurrentUser(t *testing.T)
|
||||
require.Equal(t, cfg.IssuerURL, session.ProviderKey)
|
||||
require.Equal(t, "OIDC Bind Display", session.UpstreamIdentityClaims["suggested_display_name"])
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
completion, ok := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/settings/connections", completion["redirect"])
|
||||
require.Empty(t, completion["access_token"])
|
||||
|
||||
|
||||
@@ -1129,7 +1129,7 @@ func exchangeWeChatOAuthCode(ctx context.Context, cfg wechatOAuthConfig, code st
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request wechat access token: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
@@ -1177,7 +1177,7 @@ func fetchWeChatUserInfo(ctx context.Context, tokenResp *wechatOAuthTokenRespons
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request wechat userinfo: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -155,7 +155,7 @@ func TestWeChatOAuthCallbackCreatesPendingSessionForUnifiedFlow(t *testing.T) {
|
||||
require.Equal(t, "openid-123", session.UpstreamIdentityClaims["openid"])
|
||||
}
|
||||
|
||||
func TestWeChatOAuthCallbackRejectsMissingUnionID(t *testing.T) {
|
||||
func TestWeChatOAuthCallbackFallsBackToOpenIDWhenUnionIDMissingInSingleChannelMode(t *testing.T) {
|
||||
originalAccessTokenURL := wechatOAuthAccessTokenURL
|
||||
originalUserInfoURL := wechatOAuthUserInfoURL
|
||||
t.Cleanup(func() {
|
||||
@@ -195,13 +195,22 @@ func TestWeChatOAuthCallbackRejectsMissingUnionID(t *testing.T) {
|
||||
handler.WeChatOAuthCallback(c)
|
||||
|
||||
require.Equal(t, http.StatusFound, recorder.Code)
|
||||
require.Contains(t, recorder.Header().Get("Location"), "#error=provider_error")
|
||||
require.Contains(t, recorder.Header().Get("Location"), "error_message=wechat_missing_unionid")
|
||||
require.Nil(t, findCookie(recorder.Result().Cookies(), oauthPendingSessionCookieName))
|
||||
require.Equal(t, "https://app.example.com/auth/wechat/callback", recorder.Header().Get("Location"))
|
||||
|
||||
count, err := client.PendingAuthSession.Query().Count(context.Background())
|
||||
sessionCookie := findCookie(recorder.Result().Cookies(), oauthPendingSessionCookieName)
|
||||
require.NotNil(t, sessionCookie)
|
||||
|
||||
session, err := client.PendingAuthSession.Query().
|
||||
Where(pendingauthsession.SessionTokenEQ(decodeCookieValueForTest(t, sessionCookie.Value))).
|
||||
Only(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, count)
|
||||
require.Equal(t, oauthIntentLogin, session.Intent)
|
||||
require.Equal(t, "openid-123", session.ProviderSubject)
|
||||
require.Equal(t, wechatSyntheticEmail("openid-123"), session.ResolvedEmail)
|
||||
|
||||
completion := session.LocalFlowState[oauthCompletionResponseKey].(map[string]any)
|
||||
require.Equal(t, oauthPendingChoiceStep, completion["step"])
|
||||
require.Equal(t, "third_party_signup", completion["choice_reason"])
|
||||
}
|
||||
|
||||
func TestWeChatPaymentOAuthCallbackRedirectsWithOpaqueResumeToken(t *testing.T) {
|
||||
@@ -669,7 +678,7 @@ func TestCompleteWeChatOAuthRegistrationAfterInvitationPendingSession(t *testing
|
||||
Where(pendingauthsession.SessionTokenEQ(sessionToken)).
|
||||
Only(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "invitation_required", pendingSession.LocalFlowState[oauthCompletionResponseKey].(map[string]any)["error"])
|
||||
require.Equal(t, oauthPendingChoiceStep, pendingSession.LocalFlowState[oauthCompletionResponseKey].(map[string]any)["step"])
|
||||
|
||||
body := bytes.NewBufferString(`{"invitation_code":"invite-1","adopt_display_name":true,"adopt_avatar":true}`)
|
||||
completeRecorder := httptest.NewRecorder()
|
||||
|
||||
@@ -51,22 +51,22 @@ type SystemSettings struct {
|
||||
LinuxDoConnectClientSecretConfigured bool `json:"linuxdo_connect_client_secret_configured"`
|
||||
LinuxDoConnectRedirectURL string `json:"linuxdo_connect_redirect_url"`
|
||||
|
||||
WeChatConnectEnabled bool `json:"wechat_connect_enabled"`
|
||||
WeChatConnectAppID string `json:"wechat_connect_app_id"`
|
||||
WeChatConnectAppSecretConfigured bool `json:"wechat_connect_app_secret_configured"`
|
||||
WeChatConnectOpenAppID string `json:"wechat_connect_open_app_id"`
|
||||
WeChatConnectOpenAppSecretConfigured bool `json:"wechat_connect_open_app_secret_configured"`
|
||||
WeChatConnectMPAppID string `json:"wechat_connect_mp_app_id"`
|
||||
WeChatConnectMPAppSecretConfigured bool `json:"wechat_connect_mp_app_secret_configured"`
|
||||
WeChatConnectMobileAppID string `json:"wechat_connect_mobile_app_id"`
|
||||
WeChatConnectMobileAppSecretConfigured bool `json:"wechat_connect_mobile_app_secret_configured"`
|
||||
WeChatConnectOpenEnabled bool `json:"wechat_connect_open_enabled"`
|
||||
WeChatConnectMPEnabled bool `json:"wechat_connect_mp_enabled"`
|
||||
WeChatConnectMobileEnabled bool `json:"wechat_connect_mobile_enabled"`
|
||||
WeChatConnectMode string `json:"wechat_connect_mode"`
|
||||
WeChatConnectScopes string `json:"wechat_connect_scopes"`
|
||||
WeChatConnectRedirectURL string `json:"wechat_connect_redirect_url"`
|
||||
WeChatConnectFrontendRedirectURL string `json:"wechat_connect_frontend_redirect_url"`
|
||||
WeChatConnectEnabled bool `json:"wechat_connect_enabled"`
|
||||
WeChatConnectAppID string `json:"wechat_connect_app_id"`
|
||||
WeChatConnectAppSecretConfigured bool `json:"wechat_connect_app_secret_configured"`
|
||||
WeChatConnectOpenAppID string `json:"wechat_connect_open_app_id"`
|
||||
WeChatConnectOpenAppSecretConfigured bool `json:"wechat_connect_open_app_secret_configured"`
|
||||
WeChatConnectMPAppID string `json:"wechat_connect_mp_app_id"`
|
||||
WeChatConnectMPAppSecretConfigured bool `json:"wechat_connect_mp_app_secret_configured"`
|
||||
WeChatConnectMobileAppID string `json:"wechat_connect_mobile_app_id"`
|
||||
WeChatConnectMobileAppSecretConfigured bool `json:"wechat_connect_mobile_app_secret_configured"`
|
||||
WeChatConnectOpenEnabled bool `json:"wechat_connect_open_enabled"`
|
||||
WeChatConnectMPEnabled bool `json:"wechat_connect_mp_enabled"`
|
||||
WeChatConnectMobileEnabled bool `json:"wechat_connect_mobile_enabled"`
|
||||
WeChatConnectMode string `json:"wechat_connect_mode"`
|
||||
WeChatConnectScopes string `json:"wechat_connect_scopes"`
|
||||
WeChatConnectRedirectURL string `json:"wechat_connect_redirect_url"`
|
||||
WeChatConnectFrontendRedirectURL string `json:"wechat_connect_frontend_redirect_url"`
|
||||
|
||||
OIDCConnectEnabled bool `json:"oidc_connect_enabled"`
|
||||
OIDCConnectProviderName string `json:"oidc_connect_provider_name"`
|
||||
|
||||
Reference in New Issue
Block a user