Skip to main content
Version: 2026-06-01

OAuth 2.0 Authentication Guide

CAFE24 API provides an OAuth 2.0-based secure authentication system. This guide explains step-by-step authentication methods and token management in detail.

📚 Table of Contents​


🔑 OAuth 2.0 Overview​

OAuth 2.0 is an open standard that allows third-party applications to securely access APIs without directly sharing user credentials.

Key Features​

  • Security: Does not expose user passwords
  • Permission Control: Fine-grained permission management via Scope
  • Token-based: Uses temporary Access Token
  • Refreshable: Automatic refresh via Refresh Token

Core Terms​

TermDescription
Resource OwnerShopping mall administrator (end user)
ClientApplication registered in CAFE24 App Store
Authorization ServerCAFE24 OAuth server
Resource ServerCAFE24 API server
Access TokenTemporary token representing API access permission
Refresh TokenToken for refreshing Access Token
ScopePermission range held by the token
Authorization CodeTemporary authorization code (valid for 1 minute)

🔄 Authentication Flow​

Authentication Flow


🎯 Step 1: Request Authorization Code​

Overview​

When a user approves an application, you receive an Authorization Code. This code is required to issue an Access Token.

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

Required Parameters​

ParameterDescriptionExample
mall_idShopping mall IDyourmall
response_typeResponse type (fixed)code
client_idApp's Client IDBrIfqEKoPxeE.....
redirect_uriRedirect URL after authenticationhttps://yourapp.com/callback
scopePermission scope to request (space-separated)mall.read_product mall.read_order
stateCSRF prevention token (required)random_string_12345

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={scope}

Actual Usage Example​

# Enter in web browser address bar or provide as a link
https://yourmall.cafe24api.com/api/v2/oauth/authorize?response_type=code&client_id=BrIfqEKoPxeE&state=xyz789&redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback&scope=mall.read_product+mall.read_order+mall.read_store

Success Response​

When the user approves permissions, CAFE24 server redirects to the configured redirect_uri:

HTTP/1.1 302 Found
Location: https://yourapp.com/callback?code={authorization_code}&state=xyz789

Response Parameters​

ParameterDescription
codeAuthorization Code (valid for 1 minute, single use)
stateState value sent with request (for CSRF verification)

Implementation Examples​

Node.js/Express​

const express = require('express');
const crypto = require('crypto');
const app = express();

const CLIENT_ID = process.env.CAFE24_CLIENT_ID;
const CLIENT_SECRET = process.env.CAFE24_CLIENT_SECRET;
const MALL_ID = process.env.CAFE24_MALL_ID;
const REDIRECT_URI = process.env.CAFE24_REDIRECT_URI;

// When login button is clicked
app.get('/login', (req, res) => {
// Generate State value (CSRF prevention)
const state = crypto.randomBytes(16).toString('hex');

// Save to session
req.session.oauthState = state;

// Define Scope
const scopes = ['mall.read_product', 'mall.read_order', 'mall.read_store'].join(' ');

// Create CAFE24 OAuth URL
const authUrl = new URL(`https://${MALL_ID}.cafe24api.com/api/v2/oauth/authorize`);
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('client_id', CLIENT_ID);
authUrl.searchParams.append('state', state);
authUrl.searchParams.append('redirect_uri', REDIRECT_URI);
authUrl.searchParams.append('scope', scopes);

// Redirect user to OAuth page
res.redirect(authUrl.toString());
});

Python/Flask​

from flask import Flask, session, redirect, url_for, request
from urllib.parse import urlencode
import secrets

app = Flask(__name__)

CLIENT_ID = os.getenv('CAFE24_CLIENT_ID')
CLIENT_SECRET = os.getenv('CAFE24_CLIENT_SECRET')
MALL_ID = os.getenv('CAFE24_MALL_ID')
REDIRECT_URI = os.getenv('CAFE24_REDIRECT_URI')

@app.route('/login')
def login():
# Generate State value
state = secrets.token_urlsafe(32)
session['oauth_state'] = state
session.modified = True

# Define Scope
scopes = ['mall.read_product', 'mall.read_order', 'mall.read_store']

# Configure parameters
params = {
'response_type': 'code',
'client_id': CLIENT_ID,
'state': state,
'redirect_uri': REDIRECT_URI,
'scope': ' '.join(scopes)
}

# Create OAuth URL
auth_url = f"https://{MALL_ID}.cafe24api.com/api/v2/oauth/authorize?{urlencode(params)}"

return redirect(auth_url)

🎯 Step 2: Issue Access Token​

Overview​

Use the Authorization Code to receive an Access Token required for actual API calls. This request must be made securely from the backend server.

Required Parameters​

ParameterDescriptionExample
grant_typeRequest type (fixed)authorization_code
codeAuthorization Code received from Step 1abc123...
redirect_uriSame Redirect URL used in Step 1https://yourapp.com/callback

Authorization Header​

Authorization: Basic {base64_encode({client_id}:{client_secret})}

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

Base64 Encoding Generation​

# macOS/Linux
echo -n "BrIfqEKoPxeE:xYz789" | base64
# Result: QnJJZnFFSm9QeGVFOnh5Wig0OQ==

Success Response (200 OK)​

{
"access_token": "0iqR5nM5EJIq..........",
"expires_at": "2021-03-01T14:00:00.000",
"refresh_token": "JeTJ7XpnFC0P..........",
"refresh_token_expires_at": "2021-03-15T12:00:00.000",
"client_id": "BrIfqEKoPxeE..........",
"mall_id": "yourmall",
"user_id": "test",
"scopes": [
"mall.read_order",
"mall.read_product",
"mall.read_store"
],
"issued_at": "2021-03-01T12:00:00.000",
"shop_no": "1",
"token_type": "Bearer"
}

Response Field Description​

FieldDescriptionValidity Period
access_tokenToken for API calls2 hours
refresh_tokenToken for refreshing tokens2 weeks
expires_atAccess Token expiration time-
refresh_token_expires_atRefresh Token expiration time-
token_typeToken type (always Bearer)-
scopesList of approved permissions-
issued_atToken issuance time-

Implementation Examples​

Node.js/Express​

const axios = require('axios');
const Base64 = require('js-base64').Base64;

app.get('/callback', async (req, res) => {
const { code, state } = req.query;

// Validate State
if (state !== req.session.oauthState) {
return res.status(401).json({ error: 'Invalid state' });
}

try {
// Base64 encoding
const auth = Base64.encode(`${CLIENT_ID}:${CLIENT_SECRET}`);

// Token request
const response = await axios.post(
`https://${MALL_ID}.cafe24api.com/api/v2/oauth/token`,
`grant_type=authorization_code&code=${code}&redirect_uri=${REDIRECT_URI}`,
{
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);

const tokenData = response.data;

// Save tokens to DB or session
req.session.tokens = {
access_token: tokenData.access_token,
refresh_token: tokenData.refresh_token,
expires_at: new Date(tokenData.expires_at),
refresh_token_expires_at: new Date(tokenData.refresh_token_expires_at)
};

// Save user information
req.session.user = {
mall_id: tokenData.mall_id,
user_id: tokenData.user_id,
shop_no: tokenData.shop_no,
scopes: tokenData.scopes
};

res.redirect('/dashboard');
} catch (error) {
console.error('Token exchange error:', error.response?.data);
res.status(400).json({ error: 'Failed to get access token' });
}
});

Python/Flask​

import requests
import base64
from datetime import datetime

@app.route('/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')

# Validate State
if state != session.get('oauth_state'):
return jsonify({'error': 'Invalid state'}), 401

try:
# Base64 encoding
auth_string = f"{CLIENT_ID}:{CLIENT_SECRET}"
auth_bytes = auth_string.encode('utf-8')
auth_b64 = base64.b64encode(auth_bytes).decode('utf-8')

# Token request
response = requests.post(
f"https://{MALL_ID}.cafe24api.com/api/v2/oauth/token",
data={
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI
},
headers={
'Authorization': f'Basic {auth_b64}',
'Content-Type': 'application/x-www-form-urlencoded'
}
)

token_data = response.json()

# Save tokens
session['tokens'] = {
'access_token': token_data['access_token'],
'refresh_token': token_data['refresh_token'],
'expires_at': token_data['expires_at'],
'refresh_token_expires_at': token_data['refresh_token_expires_at']
}

session['user'] = {
'mall_id': token_data['mall_id'],
'user_id': token_data['user_id'],
'shop_no': token_data['shop_no'],
'scopes': token_data['scopes']
}

session.modified = True

return redirect('/dashboard')
except Exception as e:
print(f"Token exchange error: {e}")
return jsonify({'error': 'Failed to get access token'}), 400

🎯 Step 3: API Call​

Overview​

Use the issued Access Token to call CAFE24 APIs.

Authorization Header​

Include the Access Token in Bearer token format in all API requests:

Authorization: Bearer {access_token}

Request Examples​

Retrieve Products​

curl -X GET \\
'https://yourmall.cafe24api.com/api/v2/admin/products' \\
-H 'Authorization: Bearer 0iqR5nM5EJIq..........' \\
-H 'Content-Type: application/json'

Create Order​

curl -X POST \\
'https://yourmall.cafe24api.com/api/v2/admin/orders' \\
-H 'Authorization: Bearer 0iqR5nM5EJIq..........' \\
-H 'Content-Type: application/json' \\
-d '{
"customer_name": "John Doe",
"customer_email": "john@example.com",
"items": [
{
"product_no": 128,
"quantity": 2
}
]
}'

Implementation Examples​

Node.js/Express​

async function makeApiRequest(endpoint, method = 'GET', data = null) {
const { access_token } = req.session.tokens;

const config = {
method,
url: `https://${MALL_ID}.cafe24api.com/api/v2/admin${endpoint}`,
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
}
};

if (data) {
config.data = data;
}

try {
const response = await axios(config);
return response.data;
} catch (error) {
if (error.response?.status === 401) {
// Token expired, refresh required
throw new Error('Token expired');
}
throw error;
}
}

// Usage example
app.get('/api/products', async (req, res) => {
try {
const products = await makeApiRequest('/products');
res.json(products);
} catch (error) {
res.status(500).json({ error: error.message });
}
});

🔄 Token Refresh​

Overview​

Access Token expires after 2 hours. You can use the Refresh Token to issue a new Access Token.

Important Notes​

  • Refresh Token validity period: 2 weeks
  • Automatic refresh: Requesting before Refresh Token expiration returns a new Refresh Token as well
  • Previous token revocation: Previous Refresh Token is automatically revoked when a new token is issued

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=refresh_token&refresh_token={refresh_token}'

Success Response (200 OK)​

{
"access_token": "21EZes0dGSfN..........",
"expires_at": "2021-03-01T15:50:00.000",
"refresh_token": "xLlhWztQHBik............",
"refresh_token_expires_at": "2021-03-15T13:50:00.000",
"client_id": "BrIfqEKoPxeE..........",
"mall_id": "yourmall",
"user_id": "test",
"scopes": ["mall.read_order", "mall.read_product", "mall.read_store"],
"issued_at": "2021-03-01T13:50:00.000",
"shop_no": "1",
"token_type": "Bearer"
}

Implementation Example - Auto Refresh Logic​

Node.js/Express - Middleware​

const Base64 = require('js-base64').Base64;

// Token refresh function
async function refreshAccessToken(refreshToken) {
try {
const auth = Base64.encode(`${CLIENT_ID}:${CLIENT_SECRET}`);

const response = await axios.post(
`https://${MALL_ID}.cafe24api.com/api/v2/oauth/token`,
`grant_type=refresh_token&refresh_token=${refreshToken}`,
{
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);

return response.data;
} catch (error) {
console.error('Token refresh error:', error.response?.data);
throw error;
}
}

// Middleware - Auto token refresh
app.use(async (req, res, next) => {
// Skip if no tokens
if (!req.session.tokens) {
return next();
}

const { access_token, refresh_token, expires_at } = req.session.tokens;
const now = new Date();
const expiresDate = new Date(expires_at);

// Refresh 5 minutes before token expiration
const refreshThreshold = new Date(expiresDate.getTime() - 5 * 60 * 1000);

if (now > refreshThreshold) {
try {
const newTokens = await refreshAccessToken(refresh_token);
req.session.tokens = {
access_token: newTokens.access_token,
refresh_token: newTokens.refresh_token,
expires_at: new Date(newTokens.expires_at),
refresh_token_expires_at: new Date(newTokens.refresh_token_expires_at)
};
console.log('Token refreshed successfully');
} catch (error) {
console.error('Token refresh failed, logging out user');
req.session.destroy();
return res.redirect('/login');
}
}

next();
});

Python/Flask​

def refresh_access_token(refresh_token):
"""Issue new Access Token with Refresh Token"""
try:
auth_string = f"{CLIENT_ID}:{CLIENT_SECRET}"
auth_bytes = auth_string.encode('utf-8')
auth_b64 = base64.b64encode(auth_bytes).decode('utf-8')

response = requests.post(
f"https://{MALL_ID}.cafe24api.com/api/v2/oauth/token",
data={
'grant_type': 'refresh_token',
'refresh_token': refresh_token
},
headers={
'Authorization': f'Basic {auth_b64}',
'Content-Type': 'application/x-www-form-urlencoded'
}
)

return response.json()
except Exception as e:
print(f"Token refresh error: {e}")
raise

# Flask before_request - Auto token refresh
@app.before_request
def check_and_refresh_token():
if 'tokens' not in session:
return

try:
expires_at = datetime.fromisoformat(session['tokens']['expires_at'].replace('Z', '+00:00'))
refresh_threshold = expires_at - timedelta(minutes=5)

if datetime.now(timezone.utc) > refresh_threshold or datetime.now() > expires_at.replace(tzinfo=None):
new_tokens = refresh_access_token(session['tokens']['refresh_token'])
session['tokens'] = {
'access_token': new_tokens['access_token'],
'refresh_token': new_tokens['refresh_token'],
'expires_at': new_tokens['expires_at'],
'refresh_token_expires_at': new_tokens['refresh_token_expires_at']
}
session.modified = True
print('Token refreshed successfully')
except Exception as e:
print(f'Token refresh failed: {e}')
session.clear()
return redirect('/login')

🛑 Token Revoke​

Overview​

You can explicitly revoke tokens when a user logs out or API access is no longer needed.

Important Notes​

  • Revoking an Access Token also revokes the associated Refresh Token
  • API calls with revoked tokens result in 401 Unauthorized error

Request​

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={token_hint}'

Parameters​

ParameterDescriptionExample
tokenToken to revoke (Access or Refresh)0iqR5nM5EJIq..
token_hintToken typeaccess_token or refresh_token

Implementation Examples​

Node.js/Express​

async function revokeToken(token, tokenHint = 'access_token') {
try {
const auth = Base64.encode(`${CLIENT_ID}:${CLIENT_SECRET}`);

await axios.post(
`https://${MALL_ID}.cafe24api.com/api/v2/oauth/revoke`,
`token=${token}&token_hint=${tokenHint}`,
{
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);

console.log(`${tokenHint} revoked successfully`);
} catch (error) {
console.error('Token revoke error:', error.response?.data);
throw error;
}
}

// Logout endpoint
app.get('/logout', async (req, res) => {
try {
if (req.session.tokens) {
// Revoke Access Token
await revokeToken(req.session.tokens.access_token, 'access_token');
}

req.session.destroy();
res.redirect('/login');
} catch (error) {
console.error('Logout error:', error);
// Remove session even if token revocation fails
req.session.destroy();
res.redirect('/login');
}
});

📡 Authentication Methods by API​

Admin API (OAuth 2.0)​

Authentication Method: Bearer Token

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

Requirements:

  • OAuth 2.0 authentication required
  • Access Token required

Front API​

The Front API retrieves public information such as products and categories, and does not require OAuth 2.0 authentication. Two methods are supported, and call limits differ depending on whether the request is authenticated.

Join client_id and front_api_key with a colon (:), encode the result in Base64, and place it in the Authorization header.

curl -X GET \\
'https://yourmall.cafe24api.com/api/v2/products' \\
-H 'Authorization: Basic {base64_encode({client_id}:{front_api_key})}' \\
-H 'Content-Type: application/json'

The front_api_key is issued from the Developer Center.

Generating the Base64 value

# macOS/Linux
echo -n "BrIfqEKoPxeE:frontApiKey123" | base64

Implementation Examples

// Node.js
const Base64 = require('js-base64').Base64;

const auth = Base64.encode(`${CLIENT_ID}:${FRONT_API_KEY}`);

const response = await axios.get(
`https://${MALL_ID}.cafe24api.com/api/v2/products`,
{
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json'
}
}
);
# Python
import base64
import requests

auth_string = f"{CLIENT_ID}:{FRONT_API_KEY}"
auth_b64 = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')

response = requests.get(
f"https://{MALL_ID}.cafe24api.com/api/v2/products",
headers={
'Authorization': f'Basic {auth_b64}',
'Content-Type': 'application/json'
}
)

Method 2. Unauthenticated (Client ID Header)​

This method passes only the client_id in a header.

curl -X GET \\
'https://yourmall.cafe24api.com/api/v2/products' \\
-H 'X-Cafe24-Client-Id: {client_id}' \\
-H 'Content-Type: application/json'

Comparison​

CategoryBasic Auth (Recommended)Unauthenticated
Credentialsclient_id + front_api_keyclient_id
HeaderAuthorization: Basic ...X-Cafe24-Client-Id: ...
Call limitStandard limitReduced limit
Recommended✅ For production useLimited use
tip

If both headers are sent, the Authorization header takes precedence. Use Basic authentication for stable service operation. See the API Quota Guide for call limit details.

warning

Protect front_api_key as carefully as client_secret. If your application calls the API directly from a browser, route the requests through your own server instead.

Cafe24 Analytics API (OAuth 2.0)​

Authentication Method: Bearer Token

curl -X GET \\
'https://ca-api.cafe24data.com/visitors/pageview' \\
-H 'Authorization: Bearer {access_token}' \\
-H 'Content-Type: application/json'

🛡️ Security Best Practices​

1. Protect Client Secret​

// ✅ Use environment variables
const CLIENT_SECRET = process.env.CAFE24_CLIENT_SECRET;

// .env file
CAFE24_CLIENT_ID=BrIfqEKoPxeE
CAFE24_CLIENT_SECRET=xYz789

// Add to .gitignore
echo ".env" >> .gitignore

2. HTTPS Required​

// ✅ Always use HTTPS
const redirectUri = 'https://yourapp.com/callback';

3. Prevent CSRF with State Value​

const crypto = require('crypto');
const state = crypto.randomBytes(32).toString('hex');
session.oauthState = state;

// Verify in callback
if (req.query.state !== session.oauthState) {
throw new Error('CSRF attack detected');
}

4. Token Storage Methods​

// Store tokens only in server session
req.session.tokens = {
access_token: token.access_token,
refresh_token: token.refresh_token,
expires_at: token.expires_at
};

Database Storage (Encrypted)​

CREATE TABLE user_tokens (
id INT PRIMARY KEY,
user_id VARCHAR(255),
access_token VARCHAR(255) ENCRYPTED,
refresh_token VARCHAR(255) ENCRYPTED,
expires_at DATETIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

5. Handle Token Expiration​

// Check token expiration time
const isTokenExpired = (expiresAt) => {
return new Date() > new Date(expiresAt);
};

6. Verify Permissions (Check Scope)​

const requiredScopes = ['mall.read_product', 'mall.write_order'];
const grantedScopes = tokens.scopes;

const hasAllScopes = requiredScopes.every(scope =>
grantedScopes.includes(scope)
);

if (!hasAllScopes) {
throw new Error('Insufficient permissions');
}

🐛 Error Handling and Troubleshooting​

401 Unauthorized​

Symptoms: "error": "invalid_grant" or "error": "invalid_token"

Causes:

  • Authorization Code expired (must be used within 1 minute)
  • Attempt to reuse Authorization Code
  • Incorrect Client ID/Secret
  • Token expired

Solutions:

// Attempt token refresh
if (error.response?.status === 401) {
try {
const newTokens = await refreshAccessToken(refresh_token);
// Retry with new token
} catch (refreshError) {
// Refresh failed, re-login required
res.redirect('/login');
}
}

400 Bad Request​

Symptoms: "error": "invalid_request"

Causes:

  • Missing required parameters
  • Incorrect redirect_uri
  • Content-Type error

Solutions:

// Verify all required parameters
const requiredParams = ['grant_type', 'code', 'redirect_uri'];
const hasAllParams = requiredParams.every(param => data[param]);

if (!hasAllParams) {
throw new Error('Missing required parameters');
}

403 Forbidden​

Symptoms: Insufficient Scope permissions

Causes:

  • Requested Scope was not approved
  • User denied specific permissions during consent

Solutions:

// Check requested Scope
const requiredScopes = ['mall.read_product', 'mall.write_order'];
const missingScopes = requiredScopes.filter(
scope => !tokens.scopes.includes(scope)
);

if (missingScopes.length > 0) {
console.log(`Missing scopes: ${missingScopes.join(', ')}`);
// Re-authentication required
res.redirect('/login');
}

✅ Production Checklist​

Development Phase​

  • OAuth flow tested in local development environment
  • Redirect URI tested with localhost
  • State value validation implemented
  • Token storage mechanism implemented
  • Error handling logic implemented

Security Review​

  • Client Secret stored only in environment variables
  • .env file included in .gitignore
  • HTTPS applied
  • Tokens stored only in server session
  • CSRF token (State) validation implemented
  • Token expiration handling implemented

API Testing​

  • Authorization Code issuance tested
  • Access Token issuance tested
  • API call tested (all Scopes)
  • Token refresh tested
  • Token revoke tested
  • Scope permission validation tested

Pre-Deployment​

  • Redirect URI changed to production URL
  • Redirect URI registration verified in Developer Center
  • Environment variable configuration verified
  • HTTPS certificate verified
  • Error logging configured
  • Token refresh logic tested

Monitoring​

  • Token expiration issues monitored
  • API call failures logged
  • Unauthorized errors tracked
  • Token refresh success/failure rate monitored

📚 Additional Resources​


🤝 Support​

If you encounter issues:

  1. Check the Troubleshooting section of this guide
  2. API Documentation