フロントエンドは frontend/、APIは backend/ です。テスト専用画面をバックエンドに同梱しないため、本番と開発で画面の実装がずれません。
flowchart TD
Screen[画面 / hook] --> Auth[services/auth.ts]
Auth --> Store[Secure Storage
Refresh token・IDのみ]
Auth --> API[Go API /api/v1]
API -->|Access token| Protected[保護API]
API -->|401| Refresh[Refresh tokenで更新]
Refresh -->|成功| Protected
Refresh -->|失敗| Login[ログイン画面]
Web Passkeyは、アプリの秘密tokenをURLへ載せない設計です。
sequenceDiagram
participant App as Expoアプリ
participant API as Go API
participant Web as Web Passkey画面
App->>API: bootstrap発行(Bearer pre-authまたはAccess)
API-->>App: 1分・一回限りbootstrap token
App->>Web: URL fragmentにbootstrap tokenだけ
Web->>API: options(X-Web-Passkey-Token)
API-->>Web: WebAuthn options + ceremony token
Web->>API: verify(bootstrap + ceremony headers)
API-->>Web: handoff codeだけ
Web-->>App: 許可済みdeep link
App->>API: handoff exchange + verifier + request_id
API-->>App: Access/Refresh session
URLにAccess Token、Refresh Token、pre_auth_token、ユーザーIDは入りません。Web APIの応答はキャッシュされず、ブラウザから参照元情報も送られないようにします。handoffの再送は、同じrequest_idで短時間に行った場合だけ許可されます。
開発時は frontend/.env の EXPO_PUBLIC_API_BASE_URL を http://127.0.0.1:8080/api/v1 等に設定できます。未指定でも、NODE_ENV=development のExpo Webを http://localhost または http://127.0.0.1 で開いた場合だけローカルGo API(http://127.0.0.1:8080/api/v1)を自動選択します。iPhoneを含むネイティブクライアントは、未指定なら本番APIドメイン(https://samurai-meet.disnana.com/api/v1)へ接続します。ローカルGo APIへ接続する場合は、端末から到達できるURLをEXPO_PUBLIC_API_BASE_URLへ明示してください。Expo Webを標準ポートで起動する場合はブラウザのOriginが http://localhost:8081 になるため、バックエンドも APP_ENV=development、DEV_CLIENT_ORIGIN=http://localhost:8081、WEBAUTHN_RP_ID=localhost、WEBAUTHN_RP_ORIGIN=http://localhost:8081 に揃えます。Google OAuthをローカルGo APIで完結させる場合はGOOGLE_REDIRECT_URI=http://localhost:8080/auth/callbackをGoogle Cloud Consoleへ登録し、同じcallbackをGo側へ設定します。APP_ENVだけを変更しても.envに残った本番callbackやDB接続先は切り替わりません。localhost と 127.0.0.1、ポート番号は完全一致です。本番の CLIENT_ORIGIN や本番WebAuthn OriginをローカルWebへ流用しないでください。CORSは固定Originだけを許可します。
Passkey用Web画面は、Goバックエンドの /passkey から配信します。WEB_PASSKEY_URL はこの同一Originの画面を指し、画面から相対URLでPasskey APIを呼び出します。
ネイティブクライアントの既定APIは https://samurai-meet.disnana.com/api/v1 です。EXPO_PUBLIC_API_BASE_URLを明示した場合だけ別環境へ接続します。iPhoneからローカルGo APIを確認する場合は、端末から到達できるURLを明示してください。設定しない限り、LANの8080へ自動接続しません。
募集のavailable_date、start_time、end_timeはユーザーの端末設定ではなくAsia/Tokyoとして入力・解釈します。APIのtimezoneもAsia/Tokyo固定で、空の場合はサーバーが同値へ正規化し、その他のタイムゾーンは拒否します。created_atやexpires_atなどの絶対時刻はUTC RFC3339で扱います。
以前報告された募集画面の初期表示時のinvalid_recruitment_dateは、ISO内部値とAsia/Tokyo固定化で解消し、自動テストで確認済みです。募集画面の起動から公開・応募・通知遷移・応募取消までのiOS実機全通し確認は未完了です。
プロフィールの設定では、表示言語(日本語/英語)と利用モード(local/traveler)を別々に保存します。表示言語だけを切り替えても、利用モードやログインセッションは変わりません。現在開いている募集、応募履歴、通知、詳細画面は、表示言語の変更を即時に反映します。この操作は既存データを再取得しないため、画面復帰や言語変更によって一覧が勝手に更新されることはありません。
募集・応募・通知の一覧を最新状態にしたい場合は、各画面の明示的な更新操作を使います。通知の遷移は表示文言ではなく通知の構造化IDで決めるため、日本語/英語のどちらで届いた通知も、設定変更後に開けます。
実機確認は 募集カード・マッチングの確認手順 に従います。2026-08-29時点で公開APIのhealthzとreadyz、およびiPhone上の表示言語即時切替は確認済みですが、同一手順での募集公開から応募・承認/辞退・取消・ログアウトまでの完走は未確認です。