React SDK (@auth-platform/react)#
@auth-platform/react は @auth-platform/browser を React hooks、状態 controller、headless render-prop component として公開する。UI と styling はアプリ側で構成する。
セットアップ#
pnpm add @auth-platform/react @auth-platform/browser react react-dom
pnpm add -D @types/react @types/react-dom
React 19 以上が peer dependency である。Browser client は component render ごとに生成せず、module scope などで一度だけ作り、AuthProvider に渡す。
import { createRoot } from "react-dom/client";
import { AuthProvider } from "@auth-platform/react";
import { authClient } from "./auth-client";
import { App } from "./App";
createRoot(document.getElementById("root")!).render(
<AuthProvider client={authClient}><App /></AuthProvider>,
);
AuthProvider の props は client: BrowserAuthClient と optional children?: ReactNode である。同じ client の間は sign-in flow、profile、session-list controller を保持し、client を差し替えると新しい controller 群を作る。すべての public hook と headless component は provider の内側で使う。provider 外の hook 呼び出しは Error になる。
Browser SDK を使う Client の設定では、redirect_uris に callback URI を、allowed_origins に SPA の origin を登録する。baseUrl には Browser SDK の初期化手順に示す Interaction Domain の origin を指定する。Browser client の既定 scope は openid profile email である。refreshSession()、restoreSession()、期限切れ時の自動 refresh に refresh token を使うには、scope に offline_access を含め、Client の allowed_scopes にも登録する。既定の scope では 5 分の access token 有効期限後に再認可が必要になる。設定の詳細は Browser SDK を参照する。
export interface AuthProviderProps {
readonly client: BrowserAuthClient;
readonly children?: ReactNode;
}
Hosted Login へ OIDC redirect するには useAuthClient() が返す browser client の beginHostedAuthorization() を呼ぶ。useAuth().begin() は Hosted Login redirect ではなく、React 内で email OTP/passkey を進める transaction-based headless flow を開始する。redirect callback は Browser SDK の client と同じ sessionStorage state により page reload 後に再開できる。
Hooks#
useAuthClient() / useAuthState()#
| Hook | Return |
|---|---|
useAuthClient(): BrowserAuthClient |
provider に渡した raw browser client。Hosted redirect、public config、独自 client method を使う advanced API |
useAuthState(): BrowserAuthSnapshot |
`{ status: "signed_out" |
useAuth()#
UseAuthResult は auth snapshot と transaction-driven sign-in flow の facade である。
| Field | Signature / 型 |
|---|---|
snapshot |
BrowserAuthSnapshot |
isSignedIn |
boolean |
flowState |
SignInFlowState。idle, starting, awaiting_method, email_challenge_sent, ceremony, completing, signed_in, failed |
pendingAction |
PendingAction: begin, send_email, resend_email, verify_email, passkey, social, cancel, null のいずれか |
flowError |
BrowserAuthError | null |
begin() |
Promise<Outcome<BrowserTransaction, SignInFlowError>> |
sendEmailCode(email: string) |
Promise<Outcome<{ challengeId: string }, SignInFlowError>> |
resendEmailCode() |
Promise<Outcome<{ challengeId: string }, SignInFlowError>> |
verifyEmailCode(code: string) |
Promise<Outcome<AuthenticatedSession, SignInFlowError>>。verify → complete → code exchange を行う |
signInWithPasskey() |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
registerPasskey(input?: { readonly name?: string }) |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
signInWithSocial(provider: SocialProvider) |
Promise<Outcome<{ redirected: true }, SignInFlowError>>。トップレベル遷移後は browser client の callback handler を呼ぶ |
cancelSignIn() |
Promise<Outcome<{ cancelled: true }, SignInFlowError>> |
resetSignIn() |
void。flow を local-only で idle に戻す |
getAccessToken() |
Promise<Outcome<string, BrowserAuthError>> |
refreshSession() |
Promise<Outcome<AuthenticatedSession, BrowserAuthError>> |
restoreSession() |
Promise<Outcome<AuthenticatedSession, BrowserAuthError>> |
signOut() |
Promise<Outcome<{ readonly signedOut: true }, BrowserAuthError>> |
handleRedirectCallback(callbackUrl: string) |
Promise<Outcome<AuthenticatedSession, BrowserAuthError>> |
SignInFlowError は BrowserAuthError | InvalidFlowStateError である。flow state と合わない呼び出し (例: email challenge 前の verifyEmailCode) は invalid_flow_state outcome であり、wire error と区別できる。
useEmailOtp() / usePasskey() / useSocialSignIn()#
| Hook | Return fields |
|---|---|
useEmailOtp(): UseEmailOtpResult |
challenge: EmailOtpChallenge | null (challengeId: string, email: string, expiresAt: string); pendingAction: PendingAction; error: BrowserAuthError | null |
send(email: string) |
Promise<Outcome<{ challengeId: string }, SignInFlowError>> |
resend() |
Promise<Outcome<{ challengeId: string }, SignInFlowError>> |
verify(code: string) |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
usePasskey(): UsePasskeyResult |
isSupported: boolean; isCeremonyActive: boolean; pendingAction: PendingAction; error: BrowserAuthError | null |
signInWithPasskey() |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
registerPasskey(input?: { readonly name?: string }) |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
useSocialSignIn(): UseSocialSignInResult |
signIn(provider: SocialProvider): Promise<Outcome<{ redirected: true }, SignInFlowError>>; pending: boolean; error: BrowserAuthError | null |
usePasskey().isSupported が false の場合は passkey UI を表示しない。Social sign-in は iframe/WebView に埋め込まずトップレベル redirect を使う。
useUser() / useSession()#
| Hook / field | Return type |
|---|---|
useUser(): UseUserResult |
status: UserProfileStatus (`"idle" |
reload() |
Promise<Outcome<MeUser, BrowserAuthError>> |
update(patch: MeUpdateRequest) |
Promise<Outcome<MeUser, BrowserAuthError>> |
useSession(): UseSessionResult |
snapshot: BrowserAuthSnapshot; status: SessionListStatus (`"idle" |
reload() |
Promise<Outcome<readonly MeSessionSummary[], BrowserAuthError>> |
revoke(sessionId: string) |
Promise<Outcome<{ revoked: true }, BrowserAuthError>> |
revokeAll() |
Promise<Outcome<{ revoked: true }, BrowserAuthError>> |
refreshSession() |
Promise<Outcome<AuthenticatedSession, BrowserAuthError>> |
signOut() |
Promise<Outcome<{ readonly signedOut: true }, BrowserAuthError>> |
useUser は signed-in transition で profile を読み込み、signed-out で cache を消す。useSession も signed-in 中に session list を読み込む。profile update は email 変更を行わない。email 変更は browser client の OTP-based email-change methods を使う。
Headless render-prop components#
各 component は DOM や style を描画せず、children render function に状態と handler を渡す。hook と同様、AuthProvider の内側で使う。
EmailOtpForm#
import { EmailOtpForm } from "@auth-platform/react";
function SignInForm() {
return (
<EmailOtpForm onSignedIn={(session) => console.log(session.scope)}>
{({ step, email, setEmail, code, setCode, send, resend, verify,
pendingAction, error, challengeExpiresAt }) => (
<form onSubmit={(event) => {
event.preventDefault();
if (step === "enter_email") send();
else verify();
}}>
{step === "enter_email" ? (
<input type="email" value={email} onChange={(event) => setEmail(event.target.value)} />
) : (
<>
<input inputMode="numeric" value={code} onChange={(event) => setCode(event.target.value)} />
<button type="button" onClick={resend}>再送</button>
<time>{challengeExpiresAt}</time>
</>
)}
<button disabled={pendingAction !== null}>
{step === "enter_email" ? "コード送信" : "確認"}
</button>
{error && <p role="alert">{error.type}</p>}
</form>
)}
</EmailOtpForm>
);
}
| Props / render props | Signature |
|---|---|
onSignedIn? |
(session: AuthenticatedSession) => void |
children |
(props: EmailOtpFormRenderProps) => ReactNode |
step |
`"enter_email" |
email, code |
string; setEmail(value: string), setCode(value: string) |
send, resend, verify |
() => void。verify は成功時に transaction の complete と code 交換まで実行し、onSignedIn を呼ぶ |
pendingAction |
`"send_email" |
error |
`BrowserAuthError |
challengeExpiresAt |
`string |
Component が email と code の field value を保持する。送信中の disabled state、error UI、expiry 時刻の見せ方は render function が決める。handler は void を返し、結果状態は flow の render props に反映される。
PasskeyButton#
| Props / render props | Signature |
|---|---|
mode? |
`"sign-in" |
name? |
register 時に authenticator label として渡す string |
onComplete? |
(session: AuthenticatedSession) => void |
children |
(props: PasskeyButtonRenderProps) => ReactNode |
| render props | onClick(): void, disabled, isSupported, pending, error: BrowserAuthError | null |
disabled は ceremony 中または passkey 非対応時に true となる。passkey 非対応なら render function は UI を隠すことができる。onClick は完全な sign-in/registration ceremony を開始し、成功時だけ onComplete を呼ぶ。
SocialButtons#
| Props / render props | Signature |
|---|---|
providers |
readonly SocialProvider[] |
children |
(props: SocialButtonsRenderProps) => ReactNode |
| render props | 同じ providers; signIn(provider: SocialProvider): void; pending; error: BrowserAuthError | null |
表示する provider は getPublicConfig().available_methods とアプリの要件に合わせて選ぶ。signIn handler は provider 向けのトップレベル redirect を開始する。
React-free controllers#
Controller factory は React context/provider を必要としない。状態 machine をテストや独自 UI から直接駆動する場合に使う。
createSignInFlow(client: SignInFlowClient): SignInFlow#
SignInFlowClient は BrowserAuthClient のうち createTransaction, cancelTransaction, startEmailOtp, resendEmailOtp, verifyEmailOtp, startPasskeyRegistration, startPasskeyAuthentication, startSocialSignIn, completeTransaction, exchangeAuthorizationCode を要求する。返る SignInFlow API は次のとおり。
| Method | Signature |
|---|---|
getSnapshot() |
SignInFlowSnapshot (state, pendingAction) |
subscribe(listener: SignInFlowListener): () => void |
listener を登録し、戻り値で解除する |
begin() |
Promise<Outcome<BrowserTransaction, SignInFlowError>> |
sendEmailCode(email: string) / resendEmailCode() |
Promise<Outcome<{ challengeId: string }, SignInFlowError>> |
verifyEmailCode(code: string) |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
signInWithPasskey() / registerPasskey(input?: { readonly name?: string }) |
Promise<Outcome<AuthenticatedSession, SignInFlowError>> |
signInWithSocial(provider: SocialProvider) |
Promise<Outcome<{ redirected: true }, SignInFlowError>> |
cancel() |
Promise<Outcome<{ cancelled: true }, SignInFlowError>> |
reset() |
void。server call を行わず idle に戻す |
SignInFlowState は idle → starting → awaiting_method → email_challenge_sent | ceremony → completing → signed_in の進行状態と failed を表す。PendingAction は実行中 action、SignInFlowSnapshot は state と pendingAction を持つ。Social redirect で別 page に移ると React flow instance の UI state は失われる。callback 後の code exchange は handleHostedAuthorizationCallback() が処理する。
createUserProfileController(client: UserProfileClient): UserProfileController#
UserProfileClient = Pick<BrowserAuthClient, "getMe" | "updateMe">。snapshot は { status: "idle" | "loading" | "loaded" | "error", user: MeUser | null, error: BrowserAuthError | null } である。
| Method | Signature |
|---|---|
getSnapshot() |
UserProfileSnapshot |
subscribe(listener: UserProfileListener): () => void |
listener を登録し、戻り値で解除 |
load() |
Promise<Outcome<MeUser, BrowserAuthError>>。同時 load は一 request に集約する |
update(patch: MeUpdateRequest) |
Promise<Outcome<MeUser, BrowserAuthError>> |
clear() |
void。cached profile を idle に戻す |
not_authenticated は error status ではなく idle に戻る。その他の失敗は以前取得した user を残し、error status にする。
createSessionListController(client: SessionListClient): SessionListController#
SessionListClient = Pick<BrowserAuthClient, "listMySessions" | "revokeMySession" | "revokeAllMySessions">。snapshot は { status: "idle" | "loading" | "loaded" | "error", sessions: readonly MeSessionSummary[] | null, error: BrowserAuthError | null } である。
| Method | Signature |
|---|---|
getSnapshot() |
SessionListSnapshot |
subscribe(listener: SessionListListener): () => void |
listener を登録し、戻り値で解除 |
load() |
Promise<Outcome<readonly MeSessionSummary[], BrowserAuthError>>。同時 load は一 request に集約する |
revoke(sessionId: string) |
Promise<Outcome<{ revoked: true }, BrowserAuthError>>。成功時は cache から該当 entry を除く |
revokeAll() |
Promise<Outcome<{ revoked: true }, BrowserAuthError>>。成功時は local list を idle に戻す。現在の session も server 側で revoke される |
clear() |
void。cached session list を idle に戻す |