Gate password sign-in on email verification

Unverified password identities were able to open sessions after
signing out. Reject sign-in with EMAIL_NOT_VERIFIED and add a
resend-confirmation flow so users can complete verification.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-28 10:15:25 +02:00
parent bb9fb22913
commit 5d0882778f
19 changed files with 621 additions and 60 deletions

View File

@@ -40,6 +40,15 @@ func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput)
return nil, gqlutils.Internal(ctx)
}
if !identity.EmailAddressVerified {
return nil, &gqlerror.Error{
Message: iam.NewEmailNotVerifiedError().Error(),
Extensions: map[string]any{
"code": "EMAIL_NOT_VERIFIED",
},
}
}
session := authn.SessionFromContext(ctx)
switch {
@@ -311,6 +320,19 @@ func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEm
}, nil
}
// ResendVerificationEmail is the resolver for the resendVerificationEmail field.
func (r *mutationResolver) ResendVerificationEmail(ctx context.Context, input types.ResendVerificationEmailInput) (*types.ResendVerificationEmailPayload, error) {
err := r.iam.AccountService.ResendVerificationEmail(ctx, input.Email)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot resend verification email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ResendVerificationEmailPayload{
Success: true,
}, nil
}
// ChangePassword is the resolver for the changePassword field.
func (r *mutationResolver) ChangePassword(ctx context.Context, input types.ChangePasswordInput) (*types.ChangePasswordPayload, error) {
identity := authn.IdentityFromContext(ctx)