Skip to main content
Version: 2026-09-01 (Latest)

Customer API Authentication Guide (OIDC)

The Customer API uses OpenID Connect (OIDC)-based authentication. The fundamental difference from the Admin API is that the API is called as the customer (member) themselves, not as the shopping mall administrator.

📚 Table of Contents​


🔑 Customer API Overview​

The Customer API lets a logged-in shopping mall member access their own information and orders.

CategoryIdentityAccess Scope
Admin APIShopping mall administratorAll shopping mall data
Front APIAnonymous / publicPublic information such as products and categories
Customer APIShopping mall member (self)Own member information and orders

Available Endpoints​

GroupEndpointRequired Scope
Member informationGET /customers/userinfoopenid
Order retrievalGET /customers/orders, GET /customers/orders/{order_id}, GET /customers/orders/{order_id}/itemsread_customer_order
CancellationPOST /customers/cancellationrequests, POST /customers/cancellationwrite_customer_order

The Base URL is https://{mall_id}.cafe24api.com/api/v2, the same as other APIs. See the Customer API Reference for request and response details of each endpoint.


🔍 Differences from Admin API​

ItemAdmin APICustomer API
Authenticating partyShopping mall administratorShopping mall member
Issued tokensaccess_token, refresh_tokenaccess_token, refresh_token, id_token
PKCENot supportedSupported (required for certain client profiles)
nonceNot usedSupported (replay attack prevention)
Required ScopeNoneopenid required
Scope schememall.read_order, etc.openid, read_customer_order, write_customer_order
Authentication endpoints/api/v2/oauth/authorize, /api/v2/oauth/tokenSame

The authentication endpoints themselves are the same as the Admin API. The OIDC flow is used when the requested Scope includes openid and the app is configured to use OIDC.


🔄 Authentication Flow​

Precondition: The app must be installed and activated on the shopping mall.


✅ Prerequisites​

ConditionDescription
App configured for OIDCThe app must be configured in the Developer Center to use OIDC
openid Scope requestedWithout openid in the Scope, the OIDC flow is not used and no id_token is issued
Member sessionThe session must be logged in as a shopping mall member
Operator accounts cannot be used

In an operator (administrator) session, the openid Scope is automatically removed. As a result, the OIDC flow does not take effect and no id_token is issued. Always authenticate with a member account.


🎯 Step 1: Request Authorization Code​

To obtain the Access Token needed for API calls, you first need an authorization code (code).

  • The code expires 1 minute after issuance.
  • A code that has already been used to issue an Access Token cannot be reused.

⚠️ Important: Code requests should ONLY be made in a web browser. Do not request directly using cURL or programming languages.

Parameters​

ParameterRequiredDescription
response_type✅Fixed value code
client_id✅App Key (Client ID) issued when the app was created in the Developer Center
redirect_uri✅URL that receives the code. Must exactly match one of the Redirect URLs registered in the Developer Center
scope✅Must include openid. Separated by spaces or ,
stateRecommendedRandom string for CSRF prevention. It is returned as-is on redirect, so always verify it
nonceRecommendedRandom string for replay attack prevention. Returned as-is in the nonce claim of the id_token
code_challengeConditionalRequired for clients subject to PKCE. BASE64URL(SHA256(code_verifier))
code_challenge_methodConditionalOnly S256 is allowed (plain is rejected)
promptOptionalOne of consent · login · none. none can only be used alone

prompt options​

ValueBehavior
consentShows the permission consent screen again even if already consented (e.g. when terms change)
loginForces the user to log in again even if a login session exists
noneSkips all UI interaction if a login session and permission consent already exist

Request 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

If there is no login history, the user is taken to the login screen. A login session is valid for 2 hours; if the user has logged in within that time, the consent screen is shown immediately.

Clients Subject to PKCE​

code_challenge is required for apps that have one of the following client profiles and are configured to use PKCE in the Developer Center.

Client ProfilePKCE
androidRequired
iosRequired
single-page-applicationRequired
ai-agentRequired
Others (server-side apps, etc.)Not applied
Constraints
  • Only S256 is allowed for code_challenge_method.
  • code_verifier must be a string of 43 to 128 characters consisting of A-Z a-z 0-9 - . _ ~.
  • Authentication requests are blocked in WebView environments. Mobile apps should use the system browser (Android Custom Tabs, iOS SFSafariViewController).

Generating code_verifier / code_challenge — Node.js​

const crypto = require('crypto');

// code_verifier: 43 to 128 characters
const codeVerifier = crypto.randomBytes(32).toString('base64url'); // 43 characters

// code_challenge = BASE64URL(SHA256(code_verifier))
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');

// nonce: random value for replay attack prevention
const nonce = crypto.randomBytes(16).toString('hex');

// Keep code_verifier and nonce in the session; they are needed for token exchange/validation
req.session.codeVerifier = codeVerifier;
req.session.nonce = nonce;

Generating code_verifier / code_challenge — Python​

import hashlib
import base64
import secrets

# code_verifier: 43 to 128 characters
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: random value for replay attack prevention
nonce = secrets.token_hex(16)

session['code_verifier'] = code_verifier
session['nonce'] = nonce

Response​

The server redirects to redirect_uri with the code in the query string.

HTTP/1.1 302 Found
Location: {redirect_uri}?code={authorize_code}&state={state}
KeyDescription
codeAuthorization code (valid for 1 minute, single use)
stateThe value sent in the request is returned as-is. Always confirm that it matches the value you sent

Error Response​

Errors are also delivered to redirect_uri.

HTTP/1.1 302 Found
Location: {redirect_uri}?error={error}&error_description={description}&state={state}&trace_id={trace_id}
Error CodeCauseSolution
invalid_requestclient_id, redirect_uri, or scope is missing or invalidCompare the request values against the app information in the Developer Center
unsupported_response_typeresponse_type is missing or is not codeSpecify response_type=code
invalid_scopeThe requested scope is invalid or malformedRequest only the permissions registered in the Developer Center
access_deniedThe user declined consent, or the account cannot be authenticatedGuide the user to authenticate again with a member account

trace_id is a unique serial number for error tracking. Including it in support inquiries speeds up diagnosis.


🎯 Step 2: Issue Token​

Exchange the code received in Step 1 for an Access Token.

Parameters​

ParameterRequiredDescription
grant_type✅Fixed value authorization_code
code✅Authorization code received in Step 1
redirect_uri✅Same value as in Step 1
code_verifierConditionalRequired for clients subject to PKCE. The original value used to create the code_challenge in Step 1

Request​

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}'

Success Response (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"
}
KeyDescription
access_tokenToken used for API calls (valid for 2 hours)
expires_atAccess Token expiration time
refresh_tokenToken for reissuing the Access Token (valid for 2 weeks)
refresh_token_expires_atRefresh Token expiration time
id_tokenJWT for member identification. Contains no plaintext personal information; retrieve it via GET /customers/userinfo when needed
scopesList of Scopes the user consented to

Compared with the Admin API response, id_token is added. The validity period policy is the same.

Error Response​

HTTP/1.1 400 Bad Request
{
"error": "invalid_grant",
"error_description": "It is a wrong code."
}
Error CodeHTTPCauseSolution
invalid_client401client_id/client_secret is missing or invalidCheck the Authorization: Basic header and the app information
invalid_request400redirect_uri or code is missingCheck for missing values
unsupported_grant_type400grant_type is not authorization_codeSpecify grant_type=authorization_code
invalid_grant400Wrong redirect_uri, or the code is invalid, expired, or already usedStart over from Step 1 to obtain a new code

🎯 Step 3: API Call​

Call the API with the issued Access Token in the Authorization: Bearer header.

curl -X GET \
'https://{mall_id}.cafe24api.com/api/v2/customers/orders' \
-H 'Authorization: Bearer {access_token}' \
-H 'Content-Type: application/json'

The server validates the token and its claims (iss, aud, exp, scope), confirms that the token's member (sub) owns the requested resource, and returns only that member's data.


🛡️ id_token Validation​

The id_token is a JWT signed with RS256 and contains the following claims.

ClaimDescription
issToken issuer. https://{mall_id}.cafe24api.com
audIntended audience of the token — the requesting client_id
subMember identifier
exp / iatExpiration time / issuance time
auth_timeTime when authentication was performed
amrAuthentication method. Fixed to ["pwd"] since only password authentication is currently supported
nonceIncluded only when a nonce was sent in Step 1
at_hashAccess Token hash

Validation Procedure​

Your application must verify the following before trusting an id_token.

#CheckDetails
1SignatureVerify the signature with the public key (JWKS) matching the kid in the JWT header
2issMatches https://{mall_id}.cafe24api.com
3audMatches your app's client_id
4nonceMatches the value sent in Step 1
5expConfirm the token has not expired
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 validation (if a nonce was sent in Step 1)
if (payload.nonce !== req.session.nonce) {
throw new Error('id_token nonce mismatch — possible replay attack');
}
},
);
caution

If you sent a nonce, always compare it. Skipping this check removes the protection against replay attacks.


🔐 Scope Policy​

Available Scopes​

ScopePurpose
openidRequired. Enables the OIDC flow and issues the id_token
read_customer_orderRetrieve own orders
write_customer_orderCancel own orders

Combination Rules​

✅ openid
✅ openid read_customer_order
✅ openid read_customer_order write_customer_order

❌ read_customer_order → openid missing
❌ openid mall.read_order → cannot mix with administrator Scopes
❌ openid mall.write_product → cannot mix with administrator Scopes
RuleDetails
openid requiredWithout it, the OIDC flow is not used
No mixing with administrator Scopesmall.read_* / mall.write_* Scopes cannot be used together

Calling a Customer API endpoint with a token that lacks the openid Scope returns 403 Forbidden (insufficient_scope).


🔄 Token Refresh and Revoke​

Uses the same endpoints and method as the Admin API.

Refresh​

A Refresh Token is valid for 2 weeks. On exchange, a new Refresh Token is issued and the previous one is invalidated.

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}'
Error CodeHTTPCause
invalid_client401client_id/client_secret is missing or invalid
invalid_request400refresh_token is missing
unsupported_grant_type400grant_type is not refresh_token
invalid_grant400refresh_token is invalid or expired (including a previous value that was already exchanged) → re-authenticate from Step 1

Revoke​

The Refresh Token linked to the requested token is revoked as well. If token_hint is omitted, the token is treated as an 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'
ParameterRequiredDescription
token✅Token to revoke
token_hintOptionalaccess_token · refresh_token · id_token

On success, the server responds with 200 OK and no body. Requests with an invalid token also return 200.

See OAuth 2.0 Authentication Guide — Token Refresh for details.


🌐 OIDC Discovery / Public Keys (JWKS)​

Standard OIDC metadata and the public keys for signature verification are provided. These endpoints can be called without authentication.

Discovery Document​

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"]
}

Public Keys (JWKS)​

Verify the signature with the key whose kid matches the one in the id_token header. Public keys are rotated periodically, and both the current and next keys are provided together, so implement key lookup by kid and make sure your cache can be refreshed.

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" }
]
}

🐛 Error Handling​

StatusErrorCauseSolution
400invalid_request — code_challenge missingClient is subject to PKCE but the parameter was not sentInclude code_challenge in Step 1
400invalid_request — code_challenge_methodA value other than S256 was usedSpecify S256
400invalid_request — code_verifier missingParameter was not sent during token exchangeInclude code_verifier in Step 2
400invalid_request — code_verifier format errorOutside the 43 to 128 character range, or contains disallowed charactersGenerate 43 to 128 characters using the allowed set (A-Z a-z 0-9 - . _ ~)
400Authentication request blockedAuthentication attempted from a WebViewSwitch to the system browser
401invalid_clientAuthorization: Basic header missing or app information invalidCheck the Client ID/Secret in the Developer Center
403insufficient_scopeCustomer API called without the openid ScopeAdd openid to the Scope and re-authenticate
—id_token not issuedAuthenticated with an operator account, or openid Scope missingRe-authenticate with a member account including openid

See the API Status Code Guide for other common status codes.