Customer API 認証ガイド (OIDC)
Customer APIはOpenID Connect(OIDC)ベースの認証を使用します。 ショッピングモールの管理者ではなく、顧客(会員)本人の資格でAPIを呼び出す点がAdmin APIとの根本的な違いです。
📚 目次
- Customer APIの概要
- Admin APIとの違い
- 認証フロー図
- 利用の前提条件
- Step 1: 認可コードのリクエスト
- Step 2: トークンの発行
- Step 3: API呼び出し
- id_tokenの検証
- Scopeポリシー
- トークンの再発行と失効
- OIDC Discovery / 公開鍵(JWKS)
- エラーハンドリング
🔑 Customer APIの概要
Customer APIは、ログインしたショッピングモール会員が自身の情報と注文にアクセスするためのAPIです。
| 区分 | 資格 | アクセス範囲 |
|---|---|---|
| Admin API | ショッピングモール管理者 | ショッピングモールの全データ |
| Front API | 匿名/公開 | 商品・分類などの公開情報 |
| Customer API | ショッピングモール会員本人 | 本人の会員情報・注文 |
提供エンドポイント
| グループ | エンドポイント | 必要なScope |
|---|---|---|
| 会員情報 | GET /customers/userinfo | openid |
| 注文照会 | GET /customers/orders, GET /customers/orders/{order_id}, GET /customers/orders/{order_id}/items | read_customer_order |
| キャンセル処理 | POST /customers/cancellationrequests, POST /customers/cancellation | write_customer_order |
Base URLは他のAPIと同じく https://{mall_id}.cafe24api.com/api/v2 です。
各エンドポイントのリクエスト・レスポンスの詳細はCustomer APIリファレンスを参照してください。
🔍 Admin APIとの違い
| 項目 | Admin API | Customer API |
|---|---|---|
| 認証主体 | ショッピングモール管理者 | ショッピングモール会員 |
| 発行トークン | access_token, refresh_token | access_token, refresh_token, id_token |
| PKCE | 非対応 | 対応(特定のクライアントプロファイルでは必須) |
nonce | 未使用 | 対応(リプレイ攻撃防止) |
| 必須Scope | なし | openid が必須 |
| Scope体系 | mall.read_order など | openid, read_customer_order, write_customer_order |
| 認証エンドポイント | /api/v2/oauth/authorize, /api/v2/oauth/token | 同一 |
認証エンドポイント自体はAdmin APIと同じです。リクエストScopeに openid が含まれ、アプリがOIDC利用に設定されている場合にOIDCフローとして動作します。
🔄 認証フロー図
事前条件: アプリが該当ショッピングモールにインストール・有効化されている必要があります。
✅ 利用の前提条件
| 条件 | 説明 |
|---|---|
| アプリのOIDC利用設定 | デベロッパーセンターでアプリがOIDCを利用できるよう設定されている必要があります |
openid Scopeのリクエスト | Scopeに openid がないとOIDCとして動作せず、id_token も発行されません |
| 会員セッション | ショッピングモールの会員としてログインしたセッションである必要があります |
運営者(管理者)権限のセッションでは openid Scopeが自動的に除外されます。
その結果、OIDCフローが成立せず id_token も発行されません。必ず会員アカウントで認証してください。
🎯 Step 1: 認可コードのリクエスト
API呼び出しに必要なAccess Tokenを取得するには、まず認可コード(code)を取得する必要があります。
codeは発行後1分で期限切れになります。- Access Tokenの発行に使用した
codeは再利用できません。
⚠️ 重要: コードのリクエストは必ずWebブラウザ上で行ってください。cURLやプログラミング言語から直接リクエストしないでください。
パラメータ
| パラメータ | 必須 | 説明 |
|---|---|---|
response_type | ✅ | 固定値 code |
client_id | ✅ | デベロッパーセンターでアプリ作成時に発行されたApp Key(Client ID) |
redirect_uri | ✅ | code を受け取るURL。デベロッパーセンターに登録したRedirect URLのいずれかと完全に一致する必要があります |
scope | ✅ | openid を必ず含める。スペースまたは , 区切り |
state | 推奨 | CSRF防止用のランダム文字列。リダイレクト時にそのまま返されるため必ず検証してください |
nonce | 推奨 | リプレイ攻撃防止用のランダム文字列。id_token の nonce クレームにそのまま含めて返されます |
code_challenge | 条件付き | PKCE対象クライアントでは必須。BASE64URL(SHA256(code_verifier)) |
code_challenge_method | 条件付き | S256 のみ許可(plain は拒否) |
prompt | 任意 | consent · login · none のいずれか。none は単独でのみ使用可能 |
prompt オプション
| 値 | 動作 |
|---|---|
consent | 既に同意済みでも権限同意画面を再表示(例: 規約変更時) |
login | ログインセッションがあっても再ログインを強制 |
none | ログインセッションと権限同意があればUIを表示せず対話を省略 |
リクエストURL
https://{mall_id}.cafe24api.com/api/v2/oauth/authorize
?response_type=code
&client_id={client_id}
&state={state}
&redirect_uri={redirect_uri}
&scope=openid%20read_customer_order
&nonce={nonce}
&code_challenge={code_challenge}
&code_challenge_method=S256
ログイン履歴がない場合はログイン画面に移動します。ログインセッションは2時間有効で、その間にログインした履歴があればすぐに同意画面が表示されます。
PKCE適用対象
次のクライアントプロファイルに該当し、かつデベロッパーセンターでPKCE利用に設定されたアプリでは code_challenge が必須です。
| クライアントプロファイル | PKCE |
|---|---|
android | 必須 |
ios | 必須 |
single-page-application | 必須 |
ai-agent | 必須 |
| その他(サーバーサイドアプリなど) | 非適用 |
code_challenge_methodは**S256のみ**許可されます。code_verifierはA-Z a-z 0-9 - . _ ~の文字で構成された43〜128文字の文字列である必要があります。- WebView環境では認証リクエストが遮断されます。 モバイルアプリではシステムブラウザ(Android Custom Tabs、iOS SFSafariViewController)を使用してください。
code_verifier / code_challengeの生成 — Node.js
const crypto = require('crypto');
// code_verifier: 43〜128文字
const codeVerifier = crypto.randomBytes(32).toString('base64url'); // 43文字
// code_challenge = BASE64URL(SHA256(code_verifier))
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');
// nonce: リプレイ攻撃防止用のランダム値
const nonce = crypto.randomBytes(16).toString('hex');
// code_verifierとnonceはトークン交換/検証時に必要なためセッションに保管
req.session.codeVerifier = codeVerifier;
req.session.nonce = nonce;
code_verifier / code_challengeの生成 — Python
import hashlib
import base64
import secrets
# code_verifier: 43〜128文字
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b'=').decode()
# code_challenge = BASE64URL(SHA256(code_verifier))
digest = hashlib.sha256(code_verifier.encode()).digest()
code_challenge = base64.urlsafe_b64encode(digest).rstrip(b'=').decode()
# nonce: リプレイ攻撃防止用のランダム値
nonce = secrets.token_hex(16)
session['code_verifier'] = code_verifier
session['nonce'] = nonce
レスポンス
code をクエリ文字列に含めて redirect_uri にリダイレクトします。
HTTP/1.1 302 Found
Location: {redirect_uri}?code={authorize_code}&state={state}
| キー | 説明 |
|---|---|
code | 認可コード(有効期間1分、1回のみ使用可) |
state | リクエストで送信した値がそのまま返されます。リクエスト時の値と一致するか必ず確認してください |
エラーレスポンス
エラーも redirect_uri に渡されます。
HTTP/1.1 302 Found
Location: {redirect_uri}?error={error}&error_description={description}&state={state}&trace_id={trace_id}
| エラーコード | 発生ケース | 対処方法 |
|---|---|---|
invalid_request | client_id、redirect_uri、scope の欠落または値の誤り | デベロッパーセンターのアプリ情報とリクエスト値を照合してください |
unsupported_response_type | response_type が欠落しているか code ではない | response_type=code を指定してください |
invalid_scope | リクエスト範囲が誤っているか形式エラー | デベロッパーセンターに登録した権限情報どおりにリクエストしてください |
access_denied | ユーザーが同意を拒否したか、認証不可のアカウント | 会員アカウントで再認証するよう案内してください |
trace_id はエラー追跡用の固有シリアル番号です。お問い合わせの際に併せてお伝えいただくと原因の特定が早まります。
🎯 Step 2: トークンの発行
Step 1で受け取った code をAccess Tokenと交換します。
パラメータ
| パラメータ | 必須 | 説明 |
|---|---|---|
grant_type | ✅ | 固定値 authorization_code |
code | ✅ | Step 1で受け取った認可コード |
redirect_uri | ✅ | Step 1と同じ値 |
code_verifier | 条件付き | PKCE対象クライアントでは必須。Step 1の code_challenge を作成する際に使用した元の値 |
リクエスト
curl -X POST \
'https://{mall_id}.cafe24api.com/api/v2/oauth/token' \
-H 'Authorization: Basic {base64_encode({client_id}:{client_secret})}' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code&code={code}&redirect_uri={redirect_uri}&code_verifier={code_verifier}'
成功レスポンス(200 OK)
{
"access_token": "0iqR5nM5EJIq..........",
"expires_at": "2026-08-10T14:00:00.000",
"refresh_token": "JeTJ7XpnFC0P..........",
"refresh_token_expires_at": "2026-08-24T12:00:00.000",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...",
"client_id": "BrIfqEKoPxeE..........",
"mall_id": "yourmall",
"user_id": "ecu2109510374",
"scopes": ["openid", "read_customer_order"],
"issued_at": "2026-08-10T12:00:00.000",
"shop_no": "1",
"token_type": "Bearer"
}
| キー | 説明 |
|---|---|
access_token | API呼び出しに使用するトークン(2時間有効) |
expires_at | Access Tokenの有効期限 |
refresh_token | Access Token再発行用のトークン(2週間有効) |
refresh_token_expires_at | Refresh Tokenの有効期限 |
id_token | 会員識別用のJWT。平文の個人情報は含まず、必要な場合は GET /customers/userinfo で照会 |
scopes | ユーザーが同意したScopeの一覧 |
Admin APIのレスポンスと比べて**id_token が追加**されます。有効期間のポリシーは同一です。
エラーレスポンス
HTTP/1.1 400 Bad Request
{
"error": "invalid_grant",
"error_description": "It is a wrong code."
}
| エラーコード | HTTP | 発生ケース | 対処方法 |
|---|---|---|---|
invalid_client | 401 | client_id/client_secret の欠落または誤り | Authorization: Basic ヘッダーとアプリ情報を確認してください |
invalid_request | 400 | redirect_uri、code の欠落 | 欠落した値を確認してください |
unsupported_grant_type | 400 | grant_type が authorization_code ではない | grant_type=authorization_code を指定してください |
invalid_grant | 400 | 誤った redirect_uri、または誤り・期限切れ・再利用された code | Step 1からやり直して新しい code を取得してください |
🎯 Step 3: API呼び出し
発行されたAccess Tokenを Authorization: Bearer ヘッダーに含めて呼び出します。
curl -X GET \
'https://{mall_id}.cafe24api.com/api/v2/customers/orders' \
-H 'Authorization: Bearer {access_token}' \
-H 'Content-Type: application/json'
サーバーはトークンとクレーム(iss、aud、exp、scope)を検証し、トークンの会員(sub)がリクエストされたリソースの所有者であることを確認したうえで、本人のデータのみを返します。
🛡️ id_tokenの検証
id_token はRS256で署名されたJWTで、次のクレームを含みます。
| クレーム | 説明 |
|---|---|
iss | トークン発行元(Issuer)。https://{mall_id}.cafe24api.com |
aud | トークンの受信対象 — リクエストした client_id |
sub | 会員識別子 |
exp / iat | 有効期限 / 発行時刻 |
auth_time | 認証が実行された時刻 |
amr | 認証手段。現在はパスワード認証のみ対応のため ["pwd"] 固定 |
nonce | Step 1で nonce を送信した場合のみ含まれる |
at_hash | Access Tokenのハッシュ |
検証手順
アプリケーションは id_token を信頼する前に、必ず次の項目を確認する必要があります。
| # | 検証項目 | 確認内容 |
|---|---|---|
| 1 | 署名 | JWTヘッダーの kid に対応する公開鍵(JWKS)で署名を検証 |
| 2 | iss | https://{mall_id}.cafe24api.com と一致 |
| 3 | aud | 自身のアプリの client_id と一致 |
| 4 | nonce | Step 1で送信した値と一致 |
| 5 | exp | 期限切れでないことを確認 |
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: `https://${MALL_ID}.cafe24api.com/api/v2/.well-known/jwks.json`,
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
callback(err, key && key.getPublicKey());
});
}
jwt.verify(
idToken,
getKey,
{
algorithms: ['RS256'],
issuer: `https://${MALL_ID}.cafe24api.com`,
audience: CLIENT_ID,
},
(err, payload) => {
if (err) throw err;
// nonceの検証(Step 1でnonceを送信した場合)
if (payload.nonce !== req.session.nonce) {
throw new Error('id_tokenのnonceが一致しません — リプレイ攻撃の可能性');
}
},
);
nonce を送信した場合は必ず照合してください。検証を省略するとリプレイ攻撃防止の効果が失われます。
🔐 Scopeポリシー
利用可能なScope
| Scope | 用途 |
|---|---|
openid | 必須。OIDCフローの有効化と id_token の発行 |
read_customer_order | 本人の注文照会 |
write_customer_order | 本人の注文キャンセル処理 |
組み合わせルール
✅ openid
✅ openid read_customer_order
✅ openid read_customer_order write_customer_order
❌ read_customer_order → openid が欠落
❌ openid mall.read_order → 管理者Scopeとの併用不可
❌ openid mall.write_product → 管理者Scopeとの併用不可
| ルール | 内容 |
|---|---|
openid 必須 | 含まれていない場合はOIDCフローとして動作しない |
| 管理者Scopeとの併用不可 | mall.read_* / mall.write_* 系は一緒に使用できない |
openid Scopeのないトークンで Customer APIエンドポイントを呼び出すと 403 Forbidden(insufficient_scope)が返されます。
🔄 トークンの再発行と失効
Admin APIと同じエンドポイント・方式を使用します。
再発行
Refresh Tokenは2週間有効で、交換時に新しいRefresh Tokenが発行され、以前の値は無効化されます。
curl -X POST \
'https://{mall_id}.cafe24api.com/api/v2/oauth/token' \
-H 'Authorization: Basic {base64_encode({client_id}:{client_secret})}' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=refresh_token&refresh_token={refresh_token}'
| エラーコード | HTTP | 発生ケース |
|---|---|---|
invalid_client | 401 | client_id/client_secret の欠落または誤り |
invalid_request | 400 | refresh_token の欠落 |
unsupported_grant_type | 400 | grant_type が refresh_token ではない |
invalid_grant | 400 | 誤ったまたは期限切れの refresh_token(既に交換済みの以前の値を含む)→ Step 1から再認証 |
失効
リクエストしたトークンに紐づくRefresh Tokenも併せて失効します。token_hint を省略すると access_token として扱われます。
curl -X POST \
'https://{mall_id}.cafe24api.com/api/v2/oauth/revoke' \
-H 'Authorization: Basic {base64_encode({client_id}:{client_secret})}' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'token={token}&token_hint=access_token'
| パラメータ | 必須 | 説明 |
|---|---|---|
token | ✅ | 失効させるトークン |
token_hint | 任意 | access_token · refresh_token · id_token |
成功時は 200 OK をボディなしで返します。無効なトークンでリクエストした場合も 200 を返します。
詳細はOAuth 2.0認証ガイド — トークン再発行を参照してください。
🌐 OIDC Discovery / 公開鍵(JWKS)
OIDC標準のメタデータと署名検証用の公開鍵を提供します。別途認証なしで呼び出せます。
Discoveryドキュメント
curl 'https://{mall_id}.cafe24api.com/api/v2/.well-known/openid-configuration'
{
"issuer": "https://{mall_id}.cafe24api.com",
"authorization_endpoint": "https://{mall_id}.cafe24api.com/api/v2/oauth/authorize",
"token_endpoint": "https://{mall_id}.cafe24api.com/api/v2/oauth/token",
"userinfo_endpoint": "https://{mall_id}.cafe24api.com/api/v2/customers/userinfo",
"revocation_endpoint": "https://{mall_id}.cafe24api.com/api/v2/oauth/revoke",
"jwks_uri": "https://{mall_id}.cafe24api.com/api/v2/.well-known/jwks.json",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"token_endpoint_auth_methods_supported": ["client_secret_basic"],
"code_challenge_methods_supported": ["S256"]
}
公開鍵(JWKS)
id_token ヘッダーの kid と一致する鍵で署名を検証します。公開鍵は定期的にローテーションされ、現在の鍵と次の鍵が同時に提供されるため、kid を基準に鍵を照会し、キャッシュを更新できるように実装してください。
curl 'https://{mall_id}.cafe24api.com/api/v2/.well-known/jwks.json'
{
"keys": [
{ "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "oTZnkfWCCKAK7k8bJIiAqF", "n": "0V2F...", "e": "AQAB" },
{ "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "F31cpCeCUCkOnLVwLQJQKF", "n": "oWry...", "e": "AQAB" }
]
}
🐛 エラーハンドリング
| ステータス | エラー | 原因 | 対処 |
|---|---|---|---|
| 400 | invalid_request — code_challenge の欠落 | PKCE対象クライアントなのにパラメータ未送信 | Step 1に code_challenge を含める |
| 400 | invalid_request — code_challenge_method | S256 以外の値を使用 | S256 を指定 |
| 400 | invalid_request — code_verifier の欠落 | トークン交換時にパラメータ未送信 | Step 2に code_verifier を含める |
| 400 | invalid_request — code_verifier の形式エラー | 43〜128文字の範囲外、または許可されない文字を含む | 許可文字(A-Z a-z 0-9 - . _ ~)で43〜128文字を生成 |
| 400 | 認証リクエストの遮断 | WebViewから認証を試行 | システムブラウザに切り替え |
| 401 | invalid_client | Authorization: Basic ヘッダーの欠落またはアプリ情報の誤り | デベロッパーセンターのClient ID/Secretを確認 |
| 403 | insufficient_scope | openid ScopeなしでCustomer APIを呼び出し | Scopeに openid を追加して再認証 |
| — | id_token 未発行 | 運営者アカウントでの認証、または openid Scopeの欠落 | 会員アカウントで openid を含めて再認証 |
その他の共通ステータスコードはAPI Status Codeガイドを参照してください。
📚 関連ドキュメント
- Customer APIリファレンス — エンドポイント別のリクエスト・レスポンス
- OAuth 2.0認証ガイド — Admin/Front APIの認証
- APIクォータガイド — 呼び出し制限と429への対処
- API Status Codeガイド — 全ステータスコード