본문으로 건너뛰기
버전: 2026-09-01 (최신)

Customer API 인증 가이드 (OIDC)

Customer API는 OpenID Connect(OIDC) 기반 인증을 사용합니다. 쇼핑몰 관리자가 아닌 고객(회원) 본인 자격으로 API를 호출한다는 점이 Admin API와의 근본적인 차이입니다.

📚 목차​


🔑 Customer API 개요​

Customer API는 로그인한 쇼핑몰 회원이 자신의 정보와 주문에 접근하는 API입니다.

구분자격접근 범위
Admin API쇼핑몰 관리자쇼핑몰 전체 데이터
Front API익명/공개상품·분류 등 공개 정보
Customer API쇼핑몰 회원 본인본인의 회원정보·주문

제공 엔드포인트​

그룹엔드포인트필요 Scope
회원정보GET /customers/userinfoopenid
주문 조회GET /customers/orders, GET /customers/orders/{order_id}, GET /customers/orders/{order_id}/itemsread_customer_order
취소 처리POST /customers/cancellationrequests, POST /customers/cancellationwrite_customer_order

Base URL은 다른 API와 동일하게 https://{mall_id}.cafe24api.com/api/v2 입니다. 각 엔드포인트의 요청·응답 상세는 Customer API 레퍼런스를 참고하세요.


🔍 Admin API와의 차이​

항목Admin APICustomer API
인증 주체쇼핑몰 관리자쇼핑몰 회원
발급 토큰access_token, refresh_tokenaccess_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는 재사용할 수 없습니다.

⚠️ 중요: 코드 요청은 웹 브라우저에서만 진행하세요. 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_requestclient_id, redirect_uri, scope 누락 또는 값 오류개발자센터의 앱 정보와 요청값을 대조하세요
unsupported_response_typeresponse_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_tokenAPI 호출에 사용하는 토큰 (2시간 유효)
expires_atAccess Token 만료 일시
refresh_tokenAccess Token 재발급용 토큰 (2주 유효)
refresh_token_expires_atRefresh 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_client401client_id/client_secret 누락 또는 오류Authorization: Basic 헤더와 앱 정보를 확인하세요
invalid_request400redirect_uri, code 누락누락된 값을 확인하세요
unsupported_grant_type400grant_type이 authorization_code가 아님grant_type=authorization_code로 지정하세요
invalid_grant400잘못된 redirect_uri, 잘못되었거나 만료·재사용된 codeStep 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"] 고정
nonceStep 1에서 nonce를 보낸 경우에만 포함
at_hashAccess Token 해시

검증 절차​

애플리케이션은 id_token을 신뢰하기 전에 반드시 다음을 확인해야 합니다.

#검증 항목확인 내용
1서명JWT 헤더의 kid에 해당하는 공개키(JWKS)로 서명 검증
2isshttps://{mall_id}.cafe24api.com과 일치
3aud본인 앱의 client_id와 일치
4nonceStep 1에서 보낸 값과 일치
5exp만료되지 않았는지 확인
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_* 계열은 함께 사용할 수 없음

Customer API 엔드포인트를 openid Scope 없는 토큰으로 호출하면 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_client401client_id/client_secret 누락 또는 오류
invalid_request400refresh_token 누락
unsupported_grant_type400grant_type이 refresh_token이 아님
invalid_grant400잘못되었거나 만료된 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" }
]
}

🐛 에러 처리​

상태오류원인해결
400invalid_request — code_challenge 누락PKCE 대상 클라이언트인데 파라미터 미전달Step 1에 code_challenge 포함
400invalid_request — code_challenge_methodS256 이외 값 사용S256으로 지정
400invalid_request — code_verifier 누락토큰 교환 시 파라미터 미전달Step 2에 code_verifier 포함
400invalid_request — code_verifier 형식 오류43~128자 범위를 벗어나거나 허용되지 않은 문자 포함허용 문자(A-Z a-z 0-9 - . _ ~)로 43~128자 생성
400인증 요청 차단WebView에서 인증 시도시스템 브라우저로 전환
401invalid_clientAuthorization: Basic 헤더 누락 또는 앱 정보 오류개발자센터의 Client ID/Secret 확인
403insufficient_scopeopenid Scope 없이 Customer API 호출Scope에 openid 추가 후 재인증
—id_token 미발급운영자 계정 인증 또는 openid Scope 누락회원 계정으로 openid 포함 재인증

기타 공통 상태 코드는 API Status Code 가이드를 참고하세요.


📚 관련 문서​