Files
supabase/apps/studio/data/profile/profile-identities-query.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

39 lines
1.3 KiB
TypeScript

import type { UserIdentity } from '@supabase/supabase-js'
import { useQuery } from '@tanstack/react-query'
import { profileKeys } from './keys'
import { auth } from '@/lib/gotrue'
import { UseCustomQueryOptions } from '@/types'
export async function getProfileIdentities() {
// getUser() is used instead of getSession() here because the client is configured
// with a `userStorage` option, under which getSession() can return a session whose
// `user` is a placeholder that throws when any of its properties are read.
const { error, data } = await auth.getUser()
if (error) throw error
if (!data.user) throw new Error('User not found with getUser()')
const { identities = [], email, new_email, email_change_sent_at } = data.user
return { identities, email, new_email, email_change_sent_at }
}
type ProfileIdentitiesData = {
identities: (UserIdentity & { email?: string })[]
email?: string
new_email?: string
email_change_sent_at?: string
}
type ProfileIdentitiesError = any
export const useProfileIdentitiesQuery = <TData = ProfileIdentitiesData>({
enabled = true,
...options
}: UseCustomQueryOptions<ProfileIdentitiesData, ProfileIdentitiesError, TData> = {}) => {
return useQuery<ProfileIdentitiesData, ProfileIdentitiesError, TData>({
queryKey: profileKeys.identities(),
queryFn: () => getProfileIdentities(),
...options,
})
}