React Native SDK (@auth-platform/react-native)#
@auth-platform/react-native は Expo / React Native アプリに Email OTP、Passkey、ソーシャルログイン、セッション管理を提供し、Expo Config Plugin を同梱する。
対応環境と依存関係#
Expo SDK 55 以降、React Native New Architecture を有効にした Expo Development Build または本番ビルドを使う。iOS は 15.1 以降、Android は API 28 以降かつ compile SDK 34 以降が必要である。Expo Go は Passkey、独自 scheme、Associated Domains / App Links を含む本番相当の動作確認には使えない。Bare React Native では Expo Modules を導入する。
SDK の peer dependency と宣言範囲は次のとおりである。互換性のある native module は Expo SDK に合わせて揃える。
| peer dependency | SDK の peer 範囲 |
|---|---|
react |
>=19 |
react-native |
>=0.79 |
expo |
>=55 |
expo-secure-store |
>=13 |
expo-web-browser |
>=14 |
expo-auth-session |
>=6 |
expo-linking |
>=7 |
expo-crypto |
>=14 |
react-native-passkeys |
>=0.3 |
pnpm add @auth-platform/react-native
ネイティブ module や Config Plugin を追加・変更した後は Development Build を再生成・再インストールする。JavaScript の Fast Refresh だけではネイティブ設定や module は反映されない。
クライアントの作成#
issuer は Environment の Canonical Issuer を指定する。形式は https://<issuer-host>/e/<environmentId> であり、ログイン画面を配信する Interaction Domain ではない。redirectUri はクライアントへ登録する値と完全一致させる。React Native クライアントは public client であり、アプリに client secret を含めない。SDK は PKCE を使う。
import { createReactNativeAuthClient } from "@auth-platform/react-native";
export const authClient = createReactNativeAuthClient({
issuer: "https://id.zeroword.smartcrab.ai/e/<environmentId>",
clientId: "<clientId>",
redirectUri: "com.customer.app:/oauth/callback",
scopes: ["openid", "profile", "email", "offline_access"],
rpDomain: "auth.customer.example",
});
scopes を省略すると openid profile email offline_access を使う。resource には Resource Indicator を 1 つまたは複数指定できる。rpDomain は Passkey の rp_id を固定する値で、scheme や path を含まないホスト名を指定する。environmentId は issuer からの Environment ID 抽出を上書きする場合だけ指定し、通常は省略する。fetch、now、およびネイティブ port も差し替え可能であり、通常のアプリでは既定実装を使う。
作成後に authClient.configure(config) を呼ぶ形も使える。configure() より前にクライアントメソッドや hook を呼ぶと not_configured になる。
クライアント API#
失敗時、非同期 client API の Promise は ReactNativeAuthClientError で reject する。configure() は同期 API であり、不正な issuer 設定などでは通常の Error を throw する。OTP 検証や Passkey 検証の応答から access token を直接返すのではなく、SDK が authorization code を token endpoint へ交換する。
| メソッド | 用途 |
|---|---|
getSession() |
セッション metadata を返す。未認証なら null。access token 自体は返さない |
getAccessToken(input?) |
有効な access token を返し、期限が近ければ refresh する。{ forceRefresh: true } で明示的に refresh できる |
sendEmailCode({ email }) |
Email OTP challenge を作成し、challengeId と有効期限を返す |
verifyEmailCode({ challengeId, code }) |
対応する challenge を検証し、認証 code 交換まで実行する |
signInWithPasskey() |
Passkey でサインインする |
registerPasskey({ name? }) |
認証済みユーザーに Passkey を登録する |
signInWithSocial({ provider }) |
system browser で social sign-in する |
linkSocialAccount({ provider }) |
認証済みアカウントへ social identity を link する |
getMe() / updateMe(input) |
/v1/me のユーザープロフィールを取得・更新する |
listPasskeys() / deletePasskey(passkeyId) |
Passkey を一覧・削除する |
listSessions() / revokeSession(sessionId) |
他の device session を一覧・失効する |
signOut() |
refresh token revoke を試み、network の成否にかかわらずローカル session を消去する |
signOutAll() |
/v1/me/sessions による全 session revoke を試み、その後ローカル session を消去する |
subscribe(listener) / getSnapshot() |
認証状態変更を購読・同期取得する |
configure(config) |
クライアントの設定を行う |
Email OTP の headless flow は、challenge 作成と検証を分けて呼び出す。メールアドレスと入力 code はアプリの UI から渡す。
export async function startEmailOtp(email: string) {
return authClient.sendEmailCode({ email });
}
export async function finishEmailOtp(challengeId: string, code: string) {
const result = await authClient.verifyEmailCode({ challengeId, code });
const accessToken = await authClient.getAccessToken();
return { result, accessToken };
}
sendEmailCode は challengeId と有効期限を返す。verifyEmailCode 成功時、SDK が authorization code を token endpoint へ交換し、refresh token を保存する。
実際のアプリでは code をメールから受け取り、error や rate limit を UI で扱う。signInWithSocial({ provider }) と linkSocialAccount({ provider }) では provider に google、apple、github、microsoft、generic_oidc を指定する。利用できる provider は Environment 側の設定に従う。
ソーシャル認証は expo-web-browser の auth session で system browser を開き、ephemeral session を優先する。signInWithSocial の callback は設定した redirectUri に戻り、SDK が state を検証して authorization code を交換する。iOS では custom-scheme callback を使う。iOS の AASA は webcredentials のみを提供し、applinks を含まないため、Universal Link callback は提供しない。Android の App Links association file は delegate_permission/common.handle_all_urls を含む。埋め込み WebView は使わない。
/v1/me 系で SDK が提供する操作は、プロフィール、Passkey、device session、social identity link である。Public Authentication API のその他の操作(identity 管理、email 変更、アカウント削除など)は Public Authentication API を参照する。
React Provider と hooks#
設定済み client をアプリの root で AuthProvider に渡す。Provider は client の作成・configure やアプリ状態の所有はせず、Context を提供する。
import { AuthProvider } from "@auth-platform/react-native";
import { authClient } from "./auth-client";
export function Root() {
return (
<AuthProvider client={authClient}>
<App />
</AuthProvider>
);
}
公開 hook は次のとおりである。
| hook | 主な用途 |
|---|---|
useAuth() |
認証 snapshot、Email OTP / Passkey / social sign-in、token 取得、sign-out |
useSession() |
現在のセッション metadata |
useUser() |
/v1/me プロフィールの読込・更新 |
usePasskey() |
Passkey 認証、登録、一覧・削除。端末で未対応なら passkeySupported が false |
useEmailOtp() |
Email OTP を送信・検証 |
useSocialSignIn() |
Social sign-in と identity link |
useSessionList() |
device session の一覧・失効 |
AuthProvider の外で hook を使うとエラーになる。操作 Promise は呼び出し元へ reject されるため、UI の event handler で ReactNativeAuthClientError を処理する。useUser、usePasskey、useSessionList は状態として error も公開する。
Expo Config Plugin と callback 設定#
package には app.plugin.js の entry shim があり、Config Plugin の export は @auth-platform/react-native/plugin である。Expo config の plugins へ一度だけ追加する。
export default {
name: "customer-app",
slug: "customer-app",
scheme: "com.customer.app",
ios: {
bundleIdentifier: "com.customer.app",
appleTeamId: "ABCDE12345",
},
android: { package: "com.customer.app" },
plugins: [
[
"@auth-platform/react-native/plugin",
{
clientId: "<clientId>",
rpDomain: "auth.customer.example",
callbackPath: "oauth/callback",
},
],
],
};
scheme は reverse-DNS 形式で必須である。plugin options は次のとおりである。
| option | 意味 |
|---|---|
clientId |
Environment に登録する React Native client ID |
rpDomain |
RP ID にする bare hostname。scheme / path を含めない |
callbackPath |
callback の relative path。先頭の / は含めない |
Plugin は iOS entitlements へ webcredentials:<rpDomain> と applinks:<rpDomain> を追加し、expo.scheme を CFBundleURLTypes へ登録する。Android では custom-scheme callback の intent filter、https://<rpDomain>/<callbackPath> 向けの autoVerify App Link filter、Credential Manager が読む asset_statements resource と Manifest metadata を追加する。resource は https://<rpDomain>/.well-known/assetlinks.json を参照する。iOS の AASA は webcredentials のみを提供し、applinks callback は提供しない。iOS では custom-scheme callback を使う。Android の App Links association file は delegate_permission/common.handle_all_urls を含む。
Plugin は scheme / options と、明示された最低対応未満の build 設定を build time に拒否する。Plugin 自体は iOS deployment target や Android SDK version を設定せず、AASA / assetlinks も生成・登録しない。対応下限は iOS 15.1、Android min SDK 28、compile SDK 34 であり、アプリ側の Expo / native build 設定で満たす。iOS Team ID、Bundle ID、Android package 名、署名 fingerprint は次節のとおり Environment へ登録する。
Mobile App の登録#
Mobile App の native association には、scheme、iOS Bundle ID と Apple Team ID、Android package name と SHA-256 署名 fingerprint が必要である。auth-platform react-native configure は Expo config から識別子を読み取り、署名 fingerprint を取得し、Management API の client へ登録して AASA / assetlinks と callback URI を確認する。CLI は繰り返し実行できる。option を含む手順は CLI を参照する。
Management API で管理する場合は PATCH /v1/clients/{clientId} の mobile_app に次の値を登録する。API key には該当する Management scope が必要である。
mobile_app field |
登録する値 |
|---|---|
app_scheme |
reverse-DNS 形式の custom scheme |
ios_bundle_id |
iOS Bundle ID |
ios_team_id |
Apple Team ID |
android_package_name |
Android package name |
android_sha256_fingerprints |
署名証明書の SHA-256 fingerprint。Debug / 配布用証明書ごとに登録する |
universal_link_host |
rpDomain に指定する RP ID |
詳細は Management API を参照する。
Secure storage とアプリ lifecycle#
既定の SecureStorePort は expo-secure-store を使う。SecureStore に保存するのは refresh token、token family ID、user ID / scope 等の最小限の session metadata、および認証 transaction 中だけ必要な PKCE verifier / state / transaction ID と関連する一時 metadata である。Email OTP はアプリ再起動後も検証を続けられるよう、pending transaction を完了するまで一時保存する。完了や検証失敗などで flow を閉じると消去する。access token は memory 内だけに置き、user profile 全体、provider access token、OTP は永続化しない。保存 key は Environment ID と client ID で分離される。iOS の既定保存属性は AFTER_FIRST_UNLOCK である。
access token の期限が 60 秒未満になると refresh する。同じ Environment ID / client ID の SecureStore key を共有する client instance 間では process-wide で進行中の refresh を共有し、refresh token replay を避ける。アプリが background / inactive から foreground へ戻ると token expiry を確認する。network 復帰時の refresh には networkStatus port の注入が必要であり、省略時は foreground 復帰だけが trigger になる。invalid_grant による refresh 失敗では local session を削除し、一時的な network failure では即時に削除しない。signOut() は refresh token の revoke を試みた後、network の成否にかかわらずローカル session を消去する。
Native adapter 境界とエラー#
SDK は native module の型を公開面へ漏らさず、次の port で差し替え可能にする。
| port | 契約と標準実装 |
|---|---|
PasskeyNativeAdapter |
isSupported(): Promise<boolean>、create(optionsJson: string): Promise<string>、get(optionsJson: string): Promise<string>。native credential は JSON string で受け渡し、標準実装は react-native-passkeys |
SecureStorePort |
getItemAsync(key): Promise<string | null>、setItemAsync(key, value): Promise<void>、deleteItemAsync(key): Promise<void>。標準実装は expo-secure-store |
WebBrowserPort |
openAuthSessionAsync(url, redirectUrl): Promise<WebBrowserAuthResult>。success 時は redirect URL を返し、標準実装は ephemeral session を優先する expo-web-browser auth session |
LinkingPort |
parse(url: string): ParsedCallbackUrl、getInitialURL(): Promise<string | null>、addEventListener(handler): () => void。標準実装は expo-linking |
CryptoPort |
getRandomBytes(byteCount): Uint8Array と sha256Base64(data): Promise<string>。標準実装は expo-crypto |
AppStatePort |
現在の app state と change event を提供する。subscription は remove() で解除する。標準実装は React Native AppState |
NetworkStatusPort |
subscribe(listener: (online: boolean) => void): () => void で online / offline 遷移を提供する。SDK に既定実装はなく、必要ならアプリが注入する |
失敗は ReactNativeAuthClientError として reject される。判別には message ではなく .code.type を使う。
code.type |
意味 |
|---|---|
network_error |
network failure。retryable: true |
invalid_response |
server 応答が不正 |
rate_limited |
rate limit。retryAfterSeconds がある場合は待機時間を示す |
problem_details |
Public Authentication API の RFC 9457 problem details |
oauth_error |
OAuth / OIDC error |
state_mismatch |
callback の state が一致しない |
not_authenticated |
認証が必要な操作に未認証でアクセスした |
passkey_not_supported |
端末で Passkey を利用できない |
passkey_error |
Passkey 操作失敗。reason は cancelled、failed、in_progress |
social_cancelled |
system browser で social sign-in を完了せず戻った |
invalid_flow_state |
対応する pending OTP transaction がない |
not_configured |
configure 前に client を使用した |
UI#
UI component は提供しない。公開 SDK の hooks を使ってアプリ固有の UI を実装する。