Skip to content

fix: add pending_password_set claim for invite/recovery sessions#2539

Open
deepshekhardas wants to merge 1 commit into
supabase:masterfrom
deepshekhardas:fix/pending-password-set-claim
Open

fix: add pending_password_set claim for invite/recovery sessions#2539
deepshekhardas wants to merge 1 commit into
supabase:masterfrom
deepshekhardas:fix/pending-password-set-claim

Conversation

@deepshekhardas
Copy link
Copy Markdown

Changes

Adds a \pending_password_set\ claim to JWT tokens for sessions created via invite or recovery flows before the user sets their password.

  • **\ okens/service.go**: Added \PendingPasswordSet bool\ to \AccessTokenClaims. In \GenerateAccessToken(), sets \PendingPasswordSet = true\ when the session is a recovery session or when the user has no password and was invited.
  • **\�pi/user.go**: In \UserUpdate(), checks \claims.PendingPasswordSet\ and rejects updates to email, phone, data, or app_metadata until the password is set.

Rationale

When a user is invited or goes through password recovery, the session is created via OTP/recovery verification. Before the password is set, this session should be restricted — only password updates should be allowed. RLS policies can also use the \pending_password_set\ claim to restrict data access.

Fixes #45210

Adds a pending_password_set claim to JWT tokens for sessions created
via invite or recovery flows before the user sets their password.
Enforces server-side that only password updates are allowed while
this claim is present.

Fixes #45210
@deepshekhardas deepshekhardas requested a review from a team as a code owner May 25, 2026 11:29
}

userHasPassword := params.User.HasPassword()
isPendingPasswordSet := session.IsRecovery() ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Severity: MEDIUM

session.IsRecovery() is based on immutable AMR claims and will remain true for the lifetime of a recovery session, regardless of whether the user has since set their password. This means PendingPasswordSet is permanently true for any recovery session, preventing legitimate users from ever updating email/phone/data within that session after completing the password reset — contradicting the stated intent of "before the password is set, this session should be restricted."
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: The root cause is that session.IsRecovery() is evaluated based solely on immutable AMR claims in the session, which never get cleared even after the user successfully sets a password. The fix is to also require !userHasPassword when checking the recovery session, so that isPendingPasswordSet becomes false as soon as the user has a password. Change line 699 to gate session.IsRecovery() with the !userHasPassword condition, making the intent consistent: the restriction applies only before a password exists, regardless of how the session was created.

⚠️ Experimental Feature: This code suggestion is automatically generated. Please review carefully.

Suggested change
isPendingPasswordSet := session.IsRecovery() ||
isPendingPasswordSet := (!userHasPassword && session.IsRecovery()) ||

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant