Skip to content

Connected-accounts components

The @authn-sh/sdk-react package ships three connected-accounts components in v0.4: <SocialButtons />, <PhoneNumberField />, and <ConnectedAccountsPanel />. They’re rendered as inline panels (not modal dialogs like the v0.3 security components) and handle the round-trips to the FAPI internally.

These components require <AuthnProvider> in the tree.

Renders a button per enabled OauthProvider row. Drives the OAuth first-factor flow on click — top-level browser navigation to the IdP, server-side callback handling at /v1/oauth-callback/{provider_key}, then redirect to redirectUrlComplete.

import { SocialButtons } from '@authn-sh/sdk-react';
<SocialButtons
redirectUrl={`${window.location.origin}/sso-callback`}
redirectUrlComplete={`${window.location.origin}/dashboard`}
/>
PropTypeDefaultDescription
redirectUrlstringWhere the IdP redirects on completion. Mount <AuthnRedirectCallback /> (or call Authn.handleRedirectCallback()) at this path. Required.
redirectUrlCompletestringWhere to land the user once the SignIn / SignUp completes. Defaults to Environment.display_config.after_sign_in_url.
mode'sign_in' | 'sign_up''sign_in'Whether the component drives a SignIn or a SignUp flow. <SignIn /> and <SignUp /> set this automatically.
excludedstring[][]Provider keys to hide. Useful when you want a custom layout for one provider but the defaults for the others.
appearanceAppearanceThe standard appearance object. See the appearance docs (link landing in v0.5).
SlotDescription
buttonReplaces the rendered button. Receives { provider: { key, name, iconUrl }, isLoading, onClick }.
dividerReplaces the “or” divider rendered when <SocialButtons /> is used inside <SignIn /> above the email/password form.
EventFires whenPayload
onProviderClickA button is clicked, before navigation.{ providerKey: string }
onErrorThe OAuth flow fails (oauth_state_invalid, oauth_user_cancelled, network).{ providerKey, errorCode, errorMessage }

<SocialButtons /> doesn’t fire a success event — completion lands on the page mounting <AuthnRedirectCallback />, which advances the parent SignIn / SignUp automatically.

<SignIn /> and <SignUp /> render <SocialButtons /> automatically when the environment has at least one enabled provider. Use the standalone form when you want to surface social sign-in outside the default form layout — e.g. above a custom email/password form, or on a dedicated “Connect with…” screen.

Composite input that drives add + verify in a single drop-in. Internally renders the input, the verification-code prompt after a Challenge has been issued, and the success state once PhoneNumber.verified: true.

import { PhoneNumberField } from '@authn-sh/sdk-react';
<PhoneNumberField
onVerified={(phone) => console.log('phone verified', phone)}
/>
PropTypeDefaultDescription
defaultPhoneNumberstring''Pre-fill the input. E.164 or any value the parser normalises.
onVerified(phone: PhoneNumber) => voidFires after attemptVerification succeeds and PhoneNumber.verified flips to true.
onCancel() => voidFires when the user dismisses the verification step (e.g. clicks “Use a different number”).
requireVerificationbooleantrueWhen false, the component returns the unverified PhoneNumber immediately on add. Useful for sign-up where verification is deferred.
appearanceAppearanceStandard appearance object.
SlotDescription
phoneInputReplaces the phone-number input. Receives { value, onChange, onSubmit, isSubmitting, error }.
codeInputReplaces the 6-digit verification-code input. Receives { value, onChange, onSubmit, isSubmitting, error, phoneNumber }.

idle → adding → awaiting_code → verifying → verified. Errors at verifying keep the component at awaiting_code so the user can retype the code. Calling onCancel from awaiting_code resets to idle — the unverified PhoneNumber row persists server-side and will be reused on the next add of the same number (the FAPI dedupes).

The component is used internally by <UserProfile />’s “Phone numbers” section and by <SignUp /> when the env has attribute_settings.phone_number != "off".

Lists the user’s ExternalAccount rows + the configured OauthProvider rows that are not yet connected. Shows a “Connect” button per missing provider, an “Unlink” button per existing connection.

import { ConnectedAccountsPanel } from '@authn-sh/sdk-react';
<ConnectedAccountsPanel
redirectUrl={`${window.location.origin}/sso-callback`}
redirectUrlComplete={`${window.location.origin}/account`}
/>
PropTypeDefaultDescription
redirectUrlstringWhere the IdP redirects on completion. Mount <AuthnRedirectCallback /> at this path. Required.
redirectUrlCompletestringWhere to land the user once the connect flow completes.
excludedstring[][]Provider keys to hide. Useful for env-specific layouts.
onConnect(account: ExternalAccount) => voidFires when a new ExternalAccount row is created (after the redirect bounce returns).
onUnlink(account: ExternalAccount) => voidFires after DELETE /v1/me/external-accounts/{id} succeeds.
appearanceAppearanceStandard appearance object.
SlotDescription
connectedRowReplaces the row for an already-connected provider. Receives { account: ExternalAccount, onUnlink, isUnlinking }.
availableRowReplaces the row for an available-but-not-connected provider. Receives { provider: OauthProvider, onConnect, isConnecting }.
emptyStateRenders when no providers are configured on the env.

Clicking Connect Google drives a “transfer” flow against the user’s existing session — the SDK posts an oauth_<provider_key> Challenge against a fresh SignIn with transfer: true. The IdP redirect lands at /v1/oauth-callback/<provider_key> exactly like a fresh sign-in; the resolver finds the existing User and creates a new ExternalAccount row pointing at it. No new session is created — the existing one is preserved.

Clicking Unlink GitHub calls DELETE /v1/me/external-accounts/{id}. The server makes a best-effort revocation against the IdP’s revocation_endpoint (per OIDC discovery, when present); failure to revoke does not block deletion. The user must keep at least one identifier or another sign-in method — the FAPI returns 422 if unlinking would leave them with no way to sign in.

The component is rendered inside <UserProfile />’s “Connected accounts” tab automatically when the environment has at least one enabled provider.

The SDK exposes typed hooks for each resource:

import { useExternalAccounts } from '@authn-sh/sdk-react';
const { accounts, isLoading, isLoaded } = useExternalAccounts();

Returns the live list of ExternalAccount rows for the signed-in user. Re-fetches on session change. Use this when you want to render a custom layout that <ConnectedAccountsPanel /> doesn’t cover.

import { usePhoneNumbers } from '@authn-sh/sdk-react';
const { phoneNumbers, primary, isLoading, isLoaded } = usePhoneNumbers();

Returns the user’s PhoneNumber rows. primary is the row whose ID matches User.primary_phone_number_id (null if none).

Per-row helpers exposed on the resource:

  • phone.makePrimary()PATCH { is_primary: true }.
  • phone.togglePhoneNumberReservedForSecondFactor() — flip reserved_for_second_factor.
  • phone.update({ defaultSecondFactor: true }) — set default_second_factor (requires reserved_for_second_factor: true).
  • phone.prepareVerification() — issue a phone_code Challenge against the row.
  • phone.attemptVerification({ code }) — answer the live Challenge.
  • phone.destroy()DELETE.

The v0.3 useUser() hook gains v0.4 helpers:

  • user.createPhoneNumber({ phoneNumber }) — create + return the new row.
  • user.getExternalAccounts() — alias for useExternalAccounts()’s result.

<UserProfile /> mounts both panels in v0.4:

  • Phone numbers tab — driven by <PhoneNumberField /> and usePhoneNumbers().
  • Connected accounts tab — driven by <ConnectedAccountsPanel />.

You only need the standalone components if you’re building a custom account-management surface. The default <UserProfile /> layout reads Environment.auth_config.identifier_requirements.phone_number and Environment.oauth_providers[] to decide which tabs to render, so disabling phone numbers env-wide automatically hides the tab.