ネイティブモバイルアプリ向けガイド: 複数ビジネスアカウントアクセスと OAuth フロー

OAuth Android iOS モバイル API 複数ビジネスアカウント PKCE

レシートローラーには 1 人のユーザーが複数のビジネスアカウントに所属するケースが頻繁にあります。ネイティブモバイルアプリ (Android / iOS) で「ビジネスアカウント切替」UI を実現したい場合、本ガイドのパターンを使うとログインを 1 回だけで全ビジネスアカウントのデータにアクセスできます。

仕組み

3 つの仕組みを組み合わせます。

  1. user-scoped アクセストークン — トークンが特定のビジネスアカウントに紐付かない (AuthorizedOrganizationId = null, AuthorizedUserId のみ設定)
  2. GET /api/v1/me/organizations — ユーザーが所属するビジネスアカウント一覧を返す
  3. 各 API に ?organizationId= を付与 — サーバー側でメンバーシップ検証を毎回実施

これにより、ユーザーは Web ブラウザベースの OAuth ログインを 1 回行うだけで、所属する全てのビジネスアカウントを切り替えながら参照・操作できます。

アプリ登録時の設定

このパターンを使うには、アプリ登録時に 「認可時に1つのビジネスアカウントへトークンを紐付ける」のチェックをオフにしてください。デベロッパーダッシュボードのアプリ作成画面で設定できます。

  • オンのまま (デフォルト) → 認可画面でビジネスアカウントの選択ダイアログが表示され、トークンが選択した 1 つのアカウントに紐付く (通常の Web アプリ・サーバー連携向け)
  • オフ → 認可画面でアカウント選択は出ず、user-scoped トークンが発行される (本ガイドの対象)

アプリ一覧の詳細画面では、フラグがオフのアプリには「複数アカウント対応」バッジが表示されます。

OAuth 認可フロー (PKCE + Chrome Custom Tabs / ASWebAuthenticationSession)

RFC 8252 (OAuth 2.0 for Native Apps) と OAuth 2.1 が推奨する形式に従います。アプリ内 WebView は避け、システムブラウザコンポーネントを利用してください。

  • Android: androidx.browser.customtabs.CustomTabsIntent
  • iOS: AuthenticationServices.ASWebAuthenticationSession

手順

  1. OAuth クライアントを登録
    • デベロッパーダッシュボードから手動作成: アプリを新規作成する
    • 「認可時に1つのビジネスアカウントへトークンを紐付ける」のチェックをオフにする
    • redirect_uri はカスタムスキーマ (例: com.example.rrstore://oauth/callback) または HTTPS の App Links / Universal Links を指定
  2. PKCE パラメーターを生成
    • code_verifier: 43〜128 文字のランダム文字列
    • code_challenge: BASE64URL(SHA256(code_verifier))
    • state: CSRF 対策のランダム値
  3. システムブラウザで /oauth/authorize を開く
    https://receiptroller.io/oauth/authorize
      ?response_type=code
      &client_id={your_client_id}
      &redirect_uri={your_redirect_uri}
      &scope=sales.read sns.content.read sns.audience.read analytics.read store.customers.read store.flyers.read
      &state={state}
      &code_challenge={code_challenge}
      &code_challenge_method=S256

    ユーザーは未ログインなら /Identity/Account/Login に誘導され、ログイン後に同意画面 (要求されたスコープのみ表示・ビジネスアカウント選択はなし) を経由してアプリの redirect_uri に戻ります。

    注: 「認可時に1つのビジネスアカウントへトークンを紐付ける」をオフにしていない場合、ここで「どのビジネスアカウントにアクセスするか」を選ぶプルダウンが追加で表示されます。複数アカウント対応にするには必ずオフにしてください。

  4. 認可コードをアクセストークンに交換
    POST https://receiptroller.io/api/v1/auth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    &code={code_from_redirect}
    &redirect_uri={your_redirect_uri}
    &client_id={your_client_id}
    &client_secret={your_client_secret}
    &code_verifier={code_verifier}

    レスポンスは RFC 6749 §5.1 準拠の snake_case JSON です。

    {
      "access_token": "...",
      "refresh_token": "...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "scope": "sales.read sns.content.read sns.audience.read analytics.read store.customers.read store.flyers.read"
    }

ユーザーが所属するビジネスアカウントを取得

アクセストークンを使ってビジネスアカウント一覧を取得します。

GET https://receiptroller.io/api/v1/me/organizations
Authorization: Bearer {access_token}

レスポンス:

{
  "organizations": [
    { "id": "9f0dfdc3-77ac-4bf7-9c9e-a5d1285edd88", "name": "AB", "role": "SuperUser,StoreStaff" },
    { "id": "55a5b9bf-287c-4618-a973-66b67004f292", "name": "モスフードサービス", "role": "Admin" }
  ]
}

このレスポンスをアプリ内で「ビジネスアカウント切替」UI として表示します。ユーザーが選択したビジネスアカウントの id をローカルに保持し、以降の API 呼び出しで使います。

各 API に ?organizationId= を渡す

選択中のビジネスアカウント ID をクエリパラメーターとして毎回付与します。サーバー側で「呼び出し元ユーザーがそのビジネスアカウントのメンバーか」を毎回検証します (cross-org データリークの防止)。

GET https://receiptroller.io/api/v1/sales/kpis?organizationId={selected_org_id}&storeId={store_id}
Authorization: Bearer {access_token}

複数ビジネスアカウントアクセスに対応している API 一覧

2026 年 5 月時点で以下のエンドポイント群が user-scoped トークンと ?organizationId= パターンに対応しています。

共通

  • GET /api/v1/me/organizations — 所属するビジネスアカウント一覧

売上 / 分析

  • GET /api/v1/sales/kpis
  • GET /api/v1/sales/trend
  • GET /api/v1/sales/advice
  • GET /api/v1/sales/forecast
  • GET /api/v1/sales/products — 商品 + ABC 分析
  • GET /api/v1/sales/visitors — 来店客分析
  • GET /api/v1/sales/churn — 離脱リスクスコア
  • GET /api/v1/sales/recommendations — 商品レコメンデーション
  • GET /api/v1/sales/multi-trend — 複数店舗比較

SNS

  • GET /api/v1/sns/accounts / /accounts/{id}
  • GET /api/v1/sns/posts / /posts/{id} / /posts/{id}/analytics
  • GET /api/v1/sns/comments

顧客 (CRM)

  • GET /api/v1/store/customers / /customers/{id}
  • POST /api/v1/store/customers (upsert)
  • DELETE /api/v1/store/customers/{id}
  • GET /api/v1/store/customers/{id}/purchases

チラシ

  • GET /api/v1/organizations/{orgId}/stores/{storeId}/flyers
  • GET /api/v1/organizations/{orgId}/stores/{storeId}/flyers/{flyerId}
  • POST / PUT / DELETE / publish / unpublish / expire

チラシ系は URL パスに {orgId} が含まれるため ?organizationId= の付与は不要です。サーバー側で「URL パスの orgId にユーザーがアクセス可能か」を毎回検証します。

サンプル: Android で売上 KPI を取得

// 1. ログイン後 → /api/v1/me/organizations でビジネスアカウント一覧を取得
val orgs = api.getMyOrganizations()  // Authorization: Bearer ... が付与される
showOrgSwitcher(orgs)

// 2. ユーザーがビジネスアカウントを選択
val selectedOrgId = "55a5b9bf-287c-4618-a973-66b67004f292"

// 3. 売上 KPI を取得
val kpis = api.getSalesKpis(organizationId = selectedOrgId, storeId = "abc123")
showDashboard(kpis)

// 4. ビジネスアカウント切替時はトークン再取得は不要 — selectedOrgId を変えて再リクエストするだけ
val newOrgId = "9f0dfdc3-77ac-4bf7-9c9e-a5d1285edd88"
val newKpis = api.getSalesKpis(organizationId = newOrgId, storeId = "xyz789")

セキュリティ考慮事項

  • client_secret の取り扱い — Public client (モバイルアプリ単体) の場合、client_secret はアプリバイナリに含めず PKCE のみで認証する token_endpoint_auth_method=none 構成が推奨です。
  • トークンの保存先 — Android Keystore / iOS Keychain など OS の安全領域を利用してください。
  • 有効期限 — access_token は 1 時間、refresh_token は 30 日です。refresh_token は使い捨て (ローテーション方式) で発行されます。
  • スコープ最小化 — 必要なスコープだけ要求してください。OAuth スコープ一覧
  • サーバー側のメンバーシップ検証 — ユーザーが所属しないビジネスアカウントの organizationId を指定した場合、API は 403 Forbidden を返します。

関連記事

公開日: 2026-05-26 更新日: 2026-05-30
タグ
API (10) Webhook (8) api (6) OAuth (5) oauth (5) トラブル (5) POS連携 (4) getting-started (4) アプリ登録 (4) app-registration (3)