スタートガイド

OAuth2.0 実装ガイド

はじめに

「OAuth2.0 対応」と聞くと身構えられることがありますが、Bカートの認証において押さえるべき点は3つだけです。

  • 採用しているのは 標準的な認可コードフローの Authorization Code Grant です。
  • 独自拡張はありません。一般的なOAuth クライアントライブラリがそのまま使えます。
  • アクセストークンの14日という有効期限と、その更新運用に注意します。

開発ボリュームの大半は、OAuth 部分ではなく自社システムとのデータマッピング側に発生します。

このページについて Bカートアプリの OAuth 認証に必要な情報を1ページに集約しています。 これまで PDF で提供していた「Bカートアプリ アクセストークンの発行方法」の内容も本ページに含まれます。


1. 全体像

OAuth2.0 認可コードフローのシーケンス図

ポイントは ③でBカート利用者がBカートのログイン情報を入力する先はBカート自身であることです。 御社アプリが利用者のIDやパスワードを預かる必要はありません。これが OAuth を採用している理由です。

関連資料 — アプリのインストール導線や画面要件といったフロントエンド側の仕様は、Bカートアプリストアアプリ開発手順(PDF) にまとめています。


2. 開発難易度と工数の目安

OAuth の実装自体は、全体工数の 1〜2割程度です。

以下は一般的な構成での目安です。実際の工数は、連携する自社システムの複雑さによって変わります。

フェーズ

内容

工数の目安

① OAuth 認証まわり

認可導線の実装/トークンの保管/自動リフレッシュ処理

3〜5人日

② API 連携ロジック

受注データの取得、自社システムへの投入、項目マッピング、エラー処理

要件次第(ここが大半)

③ アプリストア公開対応

アイコン・説明文・スクリーンショットの準備、デモアカウント用意、審査対応

1〜2人日

なぜ①が3〜5人日なのか

「認可URLに飛ばす → コードを受け取る → トークンに交換する」だけなら、実装は半日から1日で終わります。 残りは以下の運用設計に充てられます。

  • 契約企業(テナント)ごとにアクセストークン/リフレッシュトークンを保管する仕組み
  • 有効期限が切れる前にトークンを更新するバッチ処理
  • 更新に失敗した場合の再認可導線とエラー通知

この3点は後から足すのが難しいため、最初に設計へ織り込んでおきます。 アクセストークンの有効期限には十分ご留意下さい。


3. 実装前に押さえる3つのポイント

① トークンについて

アクセストークンの有効期限は14日間となっています。有効期限を保存し、切れる前に更新する定期バッチを用意するようにしましょう。

リフレッシュトークンには有効期限はありませんが、アクセストークンの更新時には新たなリフレッシュトークンで保存するように気をつけましょう。

更新レスポンスを受け取った後、新しいリフレッシュトークンの保存に失敗すると、 古いトークンは既に無効なため、認可のやり直しが必要です。

保存に失敗した場合は、利用者に再認可を促す導線(再連携ボタンと案内メール等)を用意しておくようにしましょう。

redirect_uri は完全一致

デベロッパーサイトで登録した値と、認可リクエスト・トークンリクエストで渡す値が完全に一致する必要があります。 末尾のスラッシュの有無、http/https の違い、ポート番号の有無、いずれも不一致の原因になります。

対策: 定数として1箇所に定義し、全リクエストで同じ値を参照します。

state の未実装

state は仕様上は任意です。ただし未実装だと CSRF 攻撃によって、 攻撃者のBカートアカウントが被害者のアプリアカウントに紐づけられるリスクが生じます。

対策: セッションに乱数を保存し、コールバック時に照合します。実装コストは数行です。


4. 事前準備

実装に入る前に、以下2点が必要です。

  1. Bカートパートナープログラムへの登録パートナープログラム登録 登録すると検証用のデモ環境が発行されます。
  2. アプリストア アカウントの発行 Bカート管理画面 > 各種設定 > 外部連携 > Bカートアプリストア アカウント管理 から発行します。

ご注意: 無料トライアルサイトからは、アプリストアアプリ公開用のデベロッパーサイトアカウントは発行できません。

準備が整ったら、デベロッパーサイト にログインし、 サイドメニューの 「アプリストア アプリ管理」 から OAuth クライアントを作成します。

作成時に入力する項目:

項目

説明

クライアント名

OAuth クライアントの識別名。利用者には表示されないため、管理しやすい名前で構いません

リダイレクトURI

認証後に戻ってくる御社アプリ側のURL。完全一致で検証されます(→ 3章④)

作成すると クライアントIDクライアントシークレット が自動発行されます。 一覧の虫眼鏡アイコンから確認できます。

関連資料 — 開発の始め方全体は アプリストア アプリ開発スタートガイド、アカウント発行の画面手順は PDF資料 をご覧ください。


5. 実装ステップ

5-1. 認可コードを取得する

御社アプリの連携ページなどから、以下のURLへ利用者をリダイレクトさせます。

認可エンドポイント

https://app.bcart.jp/oauth/authorize

リクエストパラメータ(GET)

パラメータ

必須

説明

値の例

client_id

発行されたクライアントID

95941eef-e310-4a9c-9b56-8bdf26ef8bdf

response_type

固定値 code

code

redirect_uri

登録済みのリダイレクトURI

https://example.app/callback

scope

利用するAPIのアクセス権。スペース区切りで複数指定

products-read products-write customers-read

state

任意

CSRF対策用の任意文字列。レスポンスにそのまま返却されます。実装を推奨します

6202367c31f31

組み立て例

https://app.bcart.jp/oauth/authorize?client_id=95941eef-e310-4a9c-9b56-8bdf26ef8bdf&response_type=code&redirect_uri=https%3A%2F%2Fexample.app%2Fcallback&scope=products-read%20products-write%20customers-read&state=6202367c31f31

このページで利用者にBカートへのログインが求められ、続いて御社アプリが要求したスコープを 許可するかどうかの確認画面が表示されます。

許可された場合code クエリパラメータに認可コードが付与されてリダイレクトされます。

https://example.app/callback?code=def50200ad47270…&state=6202367c31f31

キャンセル/エラーの場合error クエリパラメータが付与されます。

https://example.app/callback?error=access_denied&state=6202367c31f31

認可コードの有効期限は発行から10分間です。 受け取ったらすぐに次のステップへ進んでください。


5-2. 認可コードをアクセストークンに交換する

受け取った認可コードを、サーバーサイドから以下のエンドポイントへ POST します。 クライアントシークレットを使うため、必ずサーバー間通信で行ってください(ブラウザから実行しない)。

トークンエンドポイント

POST https://app.bcart.jp/oauth/token

リクエストパラメータ(POST)

パラメータ

説明

grant_type

固定値 authorization_code

client_id

クライアントID

client_secret

クライアントシークレット

redirect_uri

登録済みのリダイレクトURI(認可時と同一の値)

code

5-1 で取得した認可コード

成功レスポンス

{
  "token_type": "Bearer",
  "expires_in": 1209600,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbG…",
  "refresh_token": "def502006bbe5a47fcf453…"
}

フィールド

説明

token_type

Bearer 固定

expires_in

アクセストークンが無効になるまでの秒数。1209600秒=14日間

access_token

API 呼び出しに使用するアクセストークン

refresh_token

アクセストークンを更新するためのリフレッシュトークン

エラーレスポンス

{
  "error": "invalid_request",
  "error_description": "The request is missing a …",
  "hint": "Cannot decrypt the …",
  "message": "The request is missing a …"
}

デバッグのコツ error_descriptionmessage は汎用的な定型文のため、原因の切り分けには使えません。 具体的な原因は hint に入ります(例:Failed to verify code_verifier.Authorization code has been revoked)。 まず hint を確認してください。


5-3. リフレッシュトークンでアクセストークンを更新する

アクセストークンは14日で失効します。期限が切れる前に、リフレッシュトークンを使って新しいアクセストークンを取得します。 リフレッシュトークン自体に有効期限はありません。

エンドポイント(5-2 と同じ)

POST https://app.bcart.jp/oauth/token

リクエストパラメータ(POST)

パラメータ

説明

grant_type

固定値 refresh_token

refresh_token

保管しているリフレッシュトークン

client_id

クライアントID

client_secret

クライアントシークレット

成功レスポンス(5-2 と同じ形式。新しいリフレッシュトークンが含まれます

{
  "token_type": "Bearer",
  "expires_in": 1209600,
  "access_token": "eyJ0eXAiYasieQiLHIde…",
  "refresh_token": "def5020sde47fcf4dsad…"
}

リフレッシュトークンは使い捨てです

  • 更新のたびに新しいリフレッシュトークンが発行されます。次回は最新のものを使います。
  • 使用済みのリフレッシュトークンと古いアクセストークンは、更新と同時に無効になります。
  • 同じリフレッシュトークンで2回アクセストークンを取得することはできません。

つまり、更新のたびに保存し直す設計が前提になります。 並行処理が同時に更新を走らせた場合の対策は「3. 実装前に押さえる5つの落とし穴」①で解説しています。


6. 実装フロー(疑似コード)

言語非依存の最小構成です。実際にはお使いの言語の OAuth クライアントライブラリの利用を推奨します。

連携開始

function startConnect(tenant):
    state = generateRandomString()
    session.save("oauth_state", state)

    url = "https://app.bcart.jp/oauth/authorize"
        + "?client_id="     + CLIENT_ID
        + "&response_type=code"
        + "&redirect_uri="  + urlencode(REDIRECT_URI)
        + "&scope="         + urlencode("orders-read products-read")
        + "&state="         + state

    redirect(url)

コールバック受け取り

function handleCallback(request):
    if request.query["error"] exists:
        showError(request.query["error"])
        return

    if request.query["state"] != session.get("oauth_state"):
        abort("state mismatch")          // CSRF の可能性

    response = httpPost("https://app.bcart.jp/oauth/token", {
        grant_type:    "authorization_code",
        client_id:     CLIENT_ID,
        client_secret: CLIENT_SECRET,
        redirect_uri:  REDIRECT_URI,
        code:          request.query["code"]
    })

    saveTokens(
        tenantId,
        response.access_token,
        response.refresh_token,
        now() + response.expires_in      // 有効期限を保存
    )

トークン更新(定期バッチから呼ぶ)

function refreshToken(tenantId):
    lock = acquireLock("oauth:" + tenantId)   // ★ 排他制御が必須
    if not lock:
        return                                 // 別処理が更新中

    try:
        current = loadTokens(tenantId)

        response = httpPost("https://app.bcart.jp/oauth/token", {
            grant_type:    "refresh_token",
            refresh_token: current.refresh_token,
            client_id:     CLIENT_ID,
            client_secret: CLIENT_SECRET
        })

        // ★ 新しい refresh_token を必ず保存する
        saveTokens(
            tenantId,
            response.access_token,
            response.refresh_token,
            now() + response.expires_in
        )
    catch error:
        markReauthRequired(tenantId)           // ★ 再認可導線へ誘導
        notifyAdmin(tenantId, error)
    finally:
        releaseLock(lock)

定期バッチ

// 例:1日1回実行
function dailyRefreshJob():
    tenants = findTenantsWhere(expiresAt < now() + 7days)
    for tenant in tenants:
        refreshToken(tenant.id)

7. スコープ一覧

scope パラメータには、以下からアプリで必要な機能をスペース区切りで指定します。 各機能の詳細は APIドキュメント をご確認ください。

選ぶスコープは必要最小限に絞ってください。ここで要求した権限は、認可画面で右列の日本語名のまま利用者に表示されます (orders-read なら「受注参照」)。過剰な要求は許可率を下げますし、公開審査でも指摘の対象になります。

スコープ

機能

products-read

商品参照

products-write

商品書込み

product_customs-read

商品カスタム参照

product_sets-read

商品セット参照

product_sets-write

商品セット書込み

product_set_options-read

商品オプション参照

product_set_customs-read

商品セットカスタム参照

product_stock-read

在庫参照

product_stock-write

在庫書込み

categories-read

商品カテゴリ参照

categories-write

商品カテゴリ書込み

customers-read

会員参照

customers-write

会員書込み

customer_customs-read

会員カスタム参照

other_addresses-read

別配送先参照

other_addresses-write

別配送先書込み

order_statuses-read

注文状況参照

order_statuses-write

注文状況書込み

orders-read

受注参照

orders-write

受注書込み

order_products-read

受注商品参照

order_products-write

受注商品書込み

order_customs-read

受注カスタム参照

logistics-read

出荷参照

logistics-write

出荷書込み

price_groups-read

価格グループ参照

view_groups-read

表示グループ参照

shipping_groups-read

配送グループ参照

product_features-read

製品の特徴参照

product_features-write

製品の特徴書込み

bookmarks-read

お気に入り参照

bookmarks-write

お気に入り書込み

points-read

ポイント参照

points-write

ポイント書込み


8. よくある質問

Q. OAuth ライブラリは何を使えばよいですか? A. 特定のライブラリの指定はありません。Authorization Code Grant に対応した標準的なクライアントであれば どれでも動作します。多くのライブラリが PKCE をデフォルトで有効にしていますが、そのままでも問題ありません(次項参照)。

Q. PKCE には対応していますか? A. 対応しています。 code_challenge / code_verifier を用いた実装が可能です。 PKCE を必須とする設定のライブラリでも、そのままご利用いただけます。

項目

対応状況

code_challenge_method

S256 および plain

PKCE の要否

任意。code_challenge を送らない従来どおりの実装も可能です

code_verifier の検証

トークン交換時に照合されます(不一致の場合 invalid_grant/hint: Failed to verify code_verifier.

セキュリティ上、plain ではなく S256 をお使いください。

PKCE を使う場合も、トークン交換時に client_secret は必要です。 つまり PKCE を導入しても、クライアントサイド(SPA・モバイルアプリ単体)で完結する実装はできません。 client_secret を秘匿できるサーバーサイドでトークン交換を行う構成は、PKCE の有無にかかわらず必須です。

サーバーサイドで完結する一般的な構成であれば、PKCE を無理に導入する必要はありません。

Q. 検証環境はどうやって用意しますか? A. Bカートパートナープログラム にご登録いただくと、 検証用のデモ環境が発行されます。無料トライアルサイトではアプリストア用アカウントを発行できないためご注意ください。

Q. API の利用に料金はかかりますか? A. API は無料でご利用いただけます。アプリの公開も現時点では無料です (今後、有料アプリに対応する際は手数料が発生する可能性があります)。

Q. アクセストークンが切れたらどうなりますか? A. API 呼び出しが失敗します。リフレッシュトークンには有効期限がないため、 リフレッシュトークンさえ保持していれば再認可なしで復旧できます。 逆にリフレッシュトークンを失うと、利用者に再度認可してもらう必要があります。

Q. プライベートアプリとの使い分けは? A. 特定の1社にのみ提供する連携であれば、プライベートアプリ が適しています。 OAuth フローが不要で、Bカート管理画面から発行したトークンをそのまま使え、トークンは無期限です。 不特定多数のBカート利用者へ提供する場合はアプリストアアプリ(=本ガイドの OAuth 方式)を選択してください。

アプリストアアプリ

プライベートアプリ

認証方式

OAuth2.0 認可コードフロー

発行済みトークン

トークン有効期限

14日(リフレッシュで更新)

無期限

対象

不特定多数のBカート利用者

契約中の特定1社

公開審査

必要

不要

Q. アプリを特定の利用者にのみ提供したい場合は? A. アプリストアに未公開であれば第三者は利用できません。 その用途では、利用者側で発行したプライベートアプリのアクセストークンをお使いください。 なお、アプリ利用者からアプリ提供者へアプリストアアカウントを発行することは推奨していません。

関連資料 — 本ページに載っていないご質問は よくある質問 をご確認ください。


9. 次のステップ

  1. パートナープログラムに登録して検証環境を用意する
  2. デベロッパーサイトで OAuth クライアントを作成する
  3. 本ガイドの手順でアクセストークンを取得する
  4. APIドキュメントを参照して連携ロジックを実装する ※ API には利用回数の制限があります。バッチ処理の設計に影響するため、アプリ開発の際の注意点 をご確認ください。
  5. 公開申請ガイドラインを確認して公開申請する

実装でご不明な点は お問い合わせフォーム からご連絡ください。

関連ドキュメント