Problem
Applications that require a verified email before credential login currently need their own enforcement. Enabling email verification sends a token, but does not prevent an unverified user from signing in or receiving a session during signup.
Confirmed with core v0.2.2 / credential-password v0.2.1 and upstream master at 8660ad3: authenticateUser checks the password without checking EmailVerifiedAt, and automatic sign-in on signup defaults to enabled.
Proposed solution
Add an opt-in credential-plugin option:
credentialpassword.New(
credentialpassword.WithRequireEmailVerification(true),
)
Proposed behavior:
- Default to
false, preserving existing behavior.
- When enabled, check verification after successful password validation and before session creation. Reject unverified users with a distinguishable verification-required error (proposed HTTP 403), without issuing a session or cookie.
- Continue returning the ordinary invalid-credentials error for incorrect passwords, regardless of verification status.
- Apply the policy to both email and username credential sign-in, including the programmatic sign-in API.
- Let signup succeed and trigger verification, but suppress automatic sign-in while the new user remains unverified, even if auto-sign-in is otherwise enabled.
- Keep the policy scoped to credential authentication; do not change other authentication plugins or retroactively revoke existing sessions.
The HTTP sign-in handler currently maps every authentication error to invalid credentials, so it would also need to preserve the verification-required error after a correct password.
Verification and resend flow
Token verification is already public, but POST /email-verifications requires a session. Users who cannot sign in need a way to request another token after expiry or delivery failure.
Before implementation, I would like to agree on whether this should be a public email-based resend flow or a credential-authenticated resend flow. A public flow should return a generic response for unknown/already-verified addresses and include rate limiting. Documentation should cover signup → verify → login and the interaction with disabled email verification.
Alternatives considered
- A local
SessionManagerProvider wrapper can reject unverified session creation, but requires application-specific plumbing and applies more broadly than a credential-only policy.
- Middleware on protected resources permits login and a session; it does not satisfy the requirement to reject login itself.
- Disabling automatic sign-in on signup alone still permits manual login.
Affected module
- plugins/credential-password
- limen (core), if signed-out resend support is included
- docs / TypeScript client documentation
Proposed validation
Tests for unchanged defaults, verified login, unverified login with no session/cookie, incorrect passwords, username login, signup without auto-sign-in, and successful login after token verification. Any new resend path should cover expiry/retry behavior, generic responses and rate limiting.
I searched existing issues and pull requests and did not find a matching proposal. I would like to contribute a focused PR once the intended behavior and resend scope are agreed.
Problem
Applications that require a verified email before credential login currently need their own enforcement. Enabling email verification sends a token, but does not prevent an unverified user from signing in or receiving a session during signup.
Confirmed with core v0.2.2 / credential-password v0.2.1 and upstream master at
8660ad3:authenticateUserchecks the password without checkingEmailVerifiedAt, and automatic sign-in on signup defaults to enabled.Proposed solution
Add an opt-in credential-plugin option:
Proposed behavior:
false, preserving existing behavior.The HTTP sign-in handler currently maps every authentication error to invalid credentials, so it would also need to preserve the verification-required error after a correct password.
Verification and resend flow
Token verification is already public, but
POST /email-verificationsrequires a session. Users who cannot sign in need a way to request another token after expiry or delivery failure.Before implementation, I would like to agree on whether this should be a public email-based resend flow or a credential-authenticated resend flow. A public flow should return a generic response for unknown/already-verified addresses and include rate limiting. Documentation should cover signup → verify → login and the interaction with disabled email verification.
Alternatives considered
SessionManagerProviderwrapper can reject unverified session creation, but requires application-specific plumbing and applies more broadly than a credential-only policy.Affected module
Proposed validation
Tests for unchanged defaults, verified login, unverified login with no session/cookie, incorrect passwords, username login, signup without auto-sign-in, and successful login after token verification. Any new resend path should cover expiry/retry behavior, generic responses and rate limiting.
I searched existing issues and pull requests and did not find a matching proposal. I would like to contribute a focused PR once the intended behavior and resend scope are agreed.