Operator Login — サインアップ・ログイン・トークン更新・アカウント削除
倉庫スタッフ向けピッキングアプリ(picking-ios)の認証エンドポイントです。スタッフはメールアドレス + パスワード、Google サインイン(Google ID token)、Sign in with Apple(identity token)、またはメールに届く確認コードでログインして operator JWT を発行します。アカウントはプラットフォーム / 管理者側で発行し、アプリ内のセルフサインアップはありません。有効期限が切れたら refresh token で更新します。/api/operator/me はログイン中スタッフの id / email / 束縛 shop 群を返します。/api/operator/account/delete は本人が自分のアカウントを即時削除します。発行された JWT は他の Operator API(/api/operator/*)の Authorization: Bearer で使います。
Base URL
https://api.lmile.io· 認証login/login/google/login/apple/login/otp/*/refreshは不要(password/idToken/identityToken/ 確認コード /refreshTokenで認証)、me/account/deleteは operator JWT(Authorization: Bearer)(詳細)
エンドポイント
POST
/api/operator/login
email + password → JWT / refreshToken / staff / shops を発行
POST
/api/operator/login/google
Google ID token(idToken)→ JWT / refreshToken / staff / shops を発行
POST
/api/operator/login/apple
Apple identity token(identityToken)→ JWT / refreshToken / staff / shops を発行
POST
/api/operator/login/otp/request
email → 確認コードをメールで送る
POST
/api/operator/login/otp/verify
email + code(確認コード)→ JWT / refreshToken / staff / shops を発行
POST
/api/operator/refresh
refreshToken → 新 JWT / refreshToken を発行
GET
/api/operator/me
ログイン中スタッフの id / email / 束縛 shop 群
POST
/api/operator/account/delete
本人アカウントを即時削除(退会)。セッションも失効
レスポンスは camelCase(routing 系と同じ)。
/api/operator/*の認証系統(operator JWT・staff↔shop 束縛)は 認証 を参照してください。
POST /api/operator/login
スタッフのメールアドレスとパスワードを検証し、operator JWT・refresh token・スタッフ情報・束縛 shop 群を返します。束縛 shop が 0 件でも ok: true で返し(アプリ側が「shop 未割当」を案内できるように)、shops: [] になります。
リクエスト
ボディパラメータ
email
string
✓
スタッフのメールアドレス(前後空白は無視)
password
string
✓
パスワード
レスポンス
jwt
以降の /api/operator/* で Authorization: Bearer に使うアクセストークン
refreshToken
expiresAt 到来後に /api/operator/refresh へ渡す更新トークン
expiresAt
JWT の有効期限(Unix 秒。取得できない場合 null)
expiresIn
発行から失効までの秒数
tokenType
トークン種別(bearer)
staff
ログインしたスタッフの id(= ユーザー ID)/ email
shops
スタッフに束縛された shop ドメイン群(0 件は [])
shopNames
shop ドメイン → 表示名(ブランド名)の map。表示名が登録されている shop のみ含み、未登録の shop はキーに現れません(アプリはその場合 shop ドメインをそのまま表示します)。アプリの shop / 拠点選択・設定画面でドメインの代わりにブランド名を表示するために使います
挙動
認証失敗(メール不存在・パスワード不一致など)は、メールアドレスの存在有無を漏らさないため一律
401 invalid_credentialsです。試行回数の上限に達すると
429 rate_limitedを返します。認証基盤に到達できない場合は
503 auth_unavailable(旧経路にフォールバックしません)。
POST /api/operator/login/google
Google サインインで取得した Google ID token を検証し、operator JWT・refresh token・スタッフ情報・束縛 shop 群を返します(レスポンスは login と同形)。アプリ側は expo-auth-session 等で Google ID token を取得してこのエンドポイントに渡します。staff↔shop 束縛はスタッフのユーザー単位なので、同じメールアドレスのスタッフは email + password ログインと同じ束縛 shop 群に解決されます。束縛 shop が 0 件でも ok: true + shops: [] で返します。
リクエスト
ボディパラメータ
idToken
string
✓
Google サインインで取得した Google ID token(前後空白は無視)
レスポンス
フィールドの意味は POST /api/operator/login と同じです。
挙動
ID token の検証失敗(不正・失効・別 provider の token など)は、原因の詳細を漏らさないため一律
401 invalid_id_tokenです。試行回数の上限に達すると
429 rate_limitedを返します。認証基盤に到達できない場合は
503 auth_unavailable(旧経路にフォールバックしません)。
POST /api/operator/login/apple
Sign in with Apple で取得した Apple identity token を検証し、operator JWT・refresh token・スタッフ情報・束縛 shop 群を返します(レスポンスは login と同形)。アプリ側は expo-apple-authentication で identity token を取得してこのエンドポイントに渡します。Google サインイン併設に対する Apple ガイドライン 4.8(同等の代替ログイン)対応です。staff↔shop 束縛はスタッフのユーザー単位で、束縛が 0 件(自己登録直後や Hide My Email の新規ユーザー)でも ok: true + shops: [] で返し、アプリのオンボーディングで shop に紐付けます。
リクエスト
ボディパラメータ
identityToken
string
✓
Sign in with Apple で取得した Apple identity token(前後空白は無視)。認証はこの token で完結します
authorizationCode
string | null
Apple の authorization code(初回のみ。本エンドポイントの認証には未使用)
email
string | null
Apple が返すメール(初回サインイン時のみ。Hide My Email の場合は relay メール)
fullName
string | null
Apple が返す氏名(初回サインイン時のみ)
user
string
Apple の安定ユーザー ID(sub。初回のみ届く付随情報)
レスポンス
フィールドの意味は POST /api/operator/login と同じです。
挙動
認証は
identityTokenのみで完結します(email/fullName/authorizationCode/userは初回のみ届く付随情報で、検証には使いません)。identity token の検証失敗(不正・失効・別 provider の token・宛先 bundle ID 不一致など)は、原因の詳細を漏らさないため一律
401 invalid_id_tokenです。試行回数の上限に達すると
429 rate_limited、認証基盤に到達できない場合は503 auth_unavailable(フォールバックしません)。
POST /api/operator/login/otp/request
スタッフのメールアドレスへ、ログイン用の確認コードを送ります。パスワードも Google / Apple のアカウントも使わないスタッフ向けのログイン方法で、届いたコードを /api/operator/login/otp/verify に渡すとログインできます。
リクエスト
ボディパラメータ
email
string
✓
スタッフのメールアドレス(前後空白と大文字小文字は無視)
レスポンス
挙動
コードを送るのは、すでにアカウントがあるメールアドレスだけです。このエンドポイントでアカウントは作られません。
アカウントが無いメールアドレスでも
ok: trueを返します(メールアドレスの存在有無を漏らさないため)。その場合メールは届きません。届かないときは、管理者にアカウントの有無を確認してください。短い間隔で繰り返すと
429 rate_limitedを返します。少し待ってから送り直してください。認証基盤に到達できない場合は
503 auth_unavailable(フォールバックしません)。
POST /api/operator/login/otp/verify
メールに届いた確認コードを照合し、operator JWT・refresh token・スタッフ情報・束縛 shop 群を返します(レスポンスは login と同形)。束縛 shop が 0 件でも ok: true + shops: [] で返します。
リクエスト
ボディパラメータ
email
string
✓
確認コードを送ったメールアドレス
code
string
✓
メールに届いた確認コード(前後空白は無視)
レスポンス
フィールドの意味は POST /api/operator/login と同じです。
挙動
コードの誤り・期限切れなどは、原因の詳細を漏らさないため一律
401 invalid_codeです。/api/operator/login/otp/requestでコードを送り直してください。試行回数の上限に達すると
429 rate_limited、認証基盤に到達できない場合は503 auth_unavailable(フォールバックしません)。
POST /api/operator/refresh
refresh token から新しい operator JWT を発行します(再ログイン不要のセッション継続用)。
リクエスト
ボディパラメータ
refreshToken
string
✓
login / 前回の refresh で受け取った更新トークン(前後空白は無視)
レスポンス
jwt / refreshToken
新しいアクセストークン / 更新トークン(更新後はこちらを保持)
expiresAt / expiresIn / tokenType
login と同じ意味
挙動
失効済み・不正な refresh token は
401 invalid_refresh_token。試行回数の上限は
429 rate_limited、認証基盤に到達できない場合は503 auth_unavailable。
GET /api/operator/me
operator JWT を検証し、ログイン中スタッフの id / email / 束縛 shop 群を返します。ログイン直後の shop 選択画面が使います。束縛 shop が 0 件でも 200 + shops: [] を返します(「shop 未割当」を案内できるように)。
リクエスト
レスポンス
staff.id
スタッフのユーザー ID(routing write の操作者記録と同じ識別子)
staff.email
JWT の email claim(無い場合 null)
shops
スタッフに束縛された shop ドメイン群(0 件は [])
shopNames
shop ドメイン → 表示名(ブランド名)の map(login と同形)。表示名が登録されている shop のみ含み、未登録の shop はアプリがドメインをそのまま表示します
POST /api/operator/account/delete
operator JWT を検証し、ログイン中のスタッフ本人のアカウントを即時削除します(アプリ内の退会導線)。削除できるのは常に JWT の持ち主自身だけで、他人の ID を指定する経路はありません。誤操作を防ぐため、リクエストボディに confirm: true が必須です。
削除すると、そのアカウントの全セッション・refresh token が即時失効し、shop 束縛も解除されます。出荷作業などの操作履歴に残る操作者 ID は匿名の UUID として保持されます(個人を特定するメールアドレスは削除されます)。
デモアカウントは削除できません。 審査・デモ用に共有しているアカウントは保護されており
403 demo_account_protectedを返します。
リクエスト
ボディパラメータ
confirm
boolean
✓
true 固定。誤操作防止のため省略・false は 400 confirmation_required
source
string
削除の発生元メモ(監査用。例 picking-ios。最大 200 文字)
レスポンス
deleted
アカウントが削除されたら true
alreadyDeleted
既に削除済みのアカウントへの再実行で true(冪等。フィールド自体が無い場合もある)
挙動・冪等性
本人のみ: 削除対象は JWT の
sub(本人)。リクエストボディ等で他人を指定する手段はありません。即時ハード削除: アカウント(認証情報)を物理削除します。全セッション / refresh token は即時失効するため、削除後は同じ JWT で
/api/operator/*を呼べません(401)。アプリ側はローカルのトークンを破棄してログイン画面に戻してください。デモ保護: デモアカウントは
403 demo_account_protected。監査: 削除はメールアドレスのスナップショット付きで内部監査トレイルに記録されます(個人には非公開)。
冪等: 既に削除済みのアカウント(有効だが失効済みの JWT を保持したケース等)に対しても
200+deleted: trueを返します。
エラー
エンドポイント固有:
401
認証失敗(login。メール存在有無は漏らさない)
{ "ok": false, "error_kind": "invalid_credentials", "error": "invalid_credentials" }
401
確認コードが誤り / 期限切れ(login/otp/verify。原因詳細は漏らさない)
{ "ok": false, "error_kind": "invalid_code", "error": "invalid_code" }
401
ID token が不正 / 失効(login/google の Google ID token・login/apple の Apple identity token。原因詳細は漏らさない)
{ "ok": false, "error_kind": "invalid_id_token", "error": "invalid_id_token" }
401
refresh token が不正 / 失効(refresh)
{ "ok": false, "error_kind": "invalid_refresh_token", "error": "invalid_refresh_token" }
401
JWT 未提示(me)
{ "ok": false, "error_kind": "missing_token", "error": "Authorization: Bearer <jwt> required" }
401
JWT が不正 / 失効(me / account/delete)
{ "ok": false, "error_kind": "invalid_token", "error": "invalid or expired token" }
400
confirm: true 不在(account/delete)
{ "ok": false, "error_kind": "confirmation_required", "error": "confirm:true is required to delete account" }
403
デモアカウントの削除(account/delete)
{ "ok": false, "error_kind": "demo_account_protected", "error": "demo account cannot be deleted" }
500
削除監査の記録に失敗(account/delete)
{ "ok": false, "error_kind": "audit_error", "error": "failed to record deletion" }
429
試行回数の上限超過(login / login/google / login/apple / refresh)
{ "ok": false, "error_kind": "rate_limited", "error": "too many requests" }
503
認証基盤に到達できない
{ "ok": false, "error_kind": "auth_unavailable", "error": "auth service unavailable" }
共通エラー(400 / 405 / 500)は Error Codes を参照。
データモデル
スタッフのアカウント(メールアドレス・パスワード・確認状態)は Supabase Auth(
auth.users)が管理します。アカウントの作成はプラットフォーム / 管理者側で行い(アプリ内のセルフサインアップは廃止)、login/login/google/login/apple/refreshはトークンを発行します(本サービスの SoT テーブルには直接書き込みません)。sot.lm_operator_staff_shops— スタッフ(ユーザー)↔ shop の認可束縛 SoT。/api/operator/*の shop 認可(login/refreshが返すshops、各エンドポイントの403 shop_not_authorized)はこの束縛で判定します。発行されたばかりで未割当のアカウントは未束縛です(割当は Last Mile が行います)。スタッフのメールアドレス等を含むためagent_read.*/ 公開 view には露出しません(内部管理のみ)。アカウント削除時はこの束縛も連動して解除されます。sot.lm_operator_account_deletions—account/deleteの削除監査トレイル。削除した本人の ID・削除時点のメールアドレス snapshot・束縛 shop snapshot・削除時刻を記録します。メールアドレス(個人情報)を含むためagent_read.*/ 公開 view には露出しません(内部監査のみ)。
Last updated
Was this helpful?