Files
supabase/apps/studio/data/profile/profile-set-password-mutation.ts
fadymak 17dde3d324 feat(account): let OAuth-only users add a password to their account (#49057)
Allows a user to add a password which automatically creates and email
identity to enable email + password authentication for OAuth-only
accounts.

Gated behind a feature flag: `enableAccountPassword`

When a user does not have an email identity, allow them to set a
password:

<img width="762" height="284" alt="Screenshot 2026-08-13 at 14 52 53"
src="https://github.com/user-attachments/assets/70b4883a-ed38-488a-a1b7-908caa112a0b"
/>

Password modal:

<img width="519" height="423" alt="Screenshot 2026-08-13 at 14 56 57"
src="https://github.com/user-attachments/assets/56ac370d-9e6c-4b05-986f-3aa9a51826c2"
/>

Email identity has been created, allow unlinking and/or updating email
address or password:

<img width="764" height="285" alt="Screenshot 2026-08-13 at 14 55 04"
src="https://github.com/user-attachments/assets/5d9d7692-f4e3-4638-958d-15fefad01333"
/>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * OAuth-only accounts can set a password from Sign-in methods.
* Added password visibility controls, validation guidance, and success
or error feedback.
  * Sign-in methods display the account email when available.

* **Updates**
* Renamed “Account identities” to “Sign-in methods” throughout account
preferences.
* Standardized password requirements across password setup and reset
forms.
* Setting a password refreshes the current session and signs out other
sessions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-14 14:56:23 +02:00

52 lines
1.5 KiB
TypeScript

import type { AuthError } from '@supabase/auth-js'
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { profileKeys } from './keys'
import { auth } from '@/lib/gotrue'
export type SetPasswordVariables = {
password: string
}
async function setPassword({ password }: SetPasswordVariables) {
const { data, error } = await auth.updateUser({ password })
if (error) throw error
return data
}
export type SetPasswordData = Awaited<ReturnType<typeof setPassword>>
export type SetPasswordError = AuthError
export const useSetPasswordMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseMutationOptions<SetPasswordData, SetPasswordError, SetPasswordVariables>,
'mutationFn'
> = {}) => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (vars) => setPassword(vars),
async onSuccess(data, variables, context) {
// logout all other sessions after setting a password
await auth.signOut({ scope: 'others' })
await Promise.all([
auth.refreshSession(),
queryClient.invalidateQueries({ queryKey: profileKeys.identities() }),
])
await onSuccess?.(data, variables, context)
},
async onError(error, variables, context) {
if (onError === undefined) {
toast.error(`Failed to add password: ${error.message}`)
} else {
onError(error, variables, context)
}
},
...options,
})
}