회원 동의
회원을 쇼핑몰 주소의 동의 화면으로 보내 회원 본인 권한을 승인받습니다. 남의 서비스가 회원 정보를 쓸 때입니다.
업데이트됨 10.13 — 회원이 마이페이지에서 연결된 앱을 해지합니다흐름
남의 서비스가 회원 본인 권한(me:read · me:write)을 받을 때 씁니다. 동의는 쇼핑몰 주소의 화면에서 받습니다 — 회원 비밀번호가 남의 서비스로 가지 않습니다. 회원은 그 화면에서 쇼핑몰 아이디 · 비밀번호를 넣고 동의합니다(이미 쇼핑몰에 로그인해 있으면 동의만).
쇼핑몰 자신의 화면이라면
쇼핑몰이 직접 만든 화면 · 앱이 자기 로그인 폼을 쓴다면 동의 화면 없이 회원 로그인으로 바로 받습니다.
text
외부 앱(브라우저) → GET https://{shop_id}.{쇼핑몰 도메인}/oauth/authorize ?client_id &redirect_uri &scope=me:read &state &code_challenge
회원 → ① 로그인 화면(쇼핑몰 이름): 아이디 · 비밀번호 (쇼핑몰에 이미 로그인했으면 건너뜀 · 소셜 회원은 쇼핑몰 로그인을 거쳐 돌아옴)
② 동의 화면: 제공받는 자 · 제공 항목 · 이용 범위 · 목적 · 보유 기간(해지하는 곳) → [동의하고 계속]
쇼핑몰 → 302 redirect_uri?code=…&state=…
외부 앱 → POST https://api.mallstory.kr/api/oauth/token (client_id · code · code_verifier — 비밀 키 없음)
← access_token · refresh_token · subject=member · member_unique앱 준비
- 앱 종류는 브라우저 · 모바일 앱(비밀 키 없음)을 권합니다. 서버 앱도 회원 권한을 요청할 수 있지만, 그때는 토큰 교환에 비밀 키가 필요합니다.
- Redirect URI 는 외부 쇼핑몰 화면의 콜백 주소를 정확히 넣습니다(예:
https://shop.example.com/oauth/callback). - 그 쇼핑몰 관리자에서 등록한 앱만 그 쇼핑몰 회원의 동의를 받을 수 있습니다.
PKCE 준비 · 동의 주소
PKCE 는 소유자 동의와 같습니다. 주소만 쇼핑몰 주소입니다. 브라우저 · 모바일 앱도 verifier 는 기기 안(세션 저장소 등)에만 두고 서버로 보내지 않습니다.
import crypto from "node:crypto";
// 1) code_verifier (43자) · code_challenge (S256) · state
const codeVerifier = crypto.randomBytes(32).toString("base64url");
const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");
const state = crypto.randomBytes(16).toString("hex");
// 2) codeVerifier · state 를 서버 세션에 저장한다 (예: req.session.pkce = {codeVerifier, state})
// 3) 브라우저를 인가 주소로 보낸다
const url = new URL("https://{shop_id}.mallstory.kr/oauth/authorize");
url.search = new URLSearchParams({
response_type: "code",
client_id: process.env.MALLSTORY_CLIENT_ID,
redirect_uri: "https://app.example.com/oauth/callback",
scope: "shop:read",
state,
code_challenge: codeChallenge,
code_challenge_method: "S256",
}).toString();
// res.redirect(url.toString());scope 바꾸기
예시의
shop:read 를 me:read me:write 로 바꿔 쓰세요. 회원 권한과 관리 권한을 섞으면 invalid_scope 입니다.동의 화면 · 토큰 교환
GET
https://{shop_id}.{쇼핑몰 도메인}/oauth/authorize- 파라미터는 소유자 동의와 같습니다.
scope에는 회원 권한만 넣습니다(예:me:read me:write). - 회원이 로그인 화면에서 아이디 · 비밀번호를 넣고, 다음 동의 화면에서 [동의하고 계속] 을 누르면 code 가 돌아옵니다. 쇼핑몰에 이미 로그인한 브라우저면 로그인 화면을 건너뜁니다.
- 동의 화면의 이용 목적 · 개인정보처리방침 링크는 앱 등록 때 적은 값입니다(회원 권한을 켜면 칸이 나옵니다). 개인정보보호법의 제3자 제공 고지라 꼭 채우세요.
- 한 번 동의한 범위는 다시 묻지 않습니다 — 로그인만 하면 바로 돌아갑니다(쇼핑몰에 로그인돼 있으면 화면 없이). 새 권한 · 이용 목적이나 개인정보처리방침이 바뀌면 다시 묻습니다. 늘 묻게 하려면
prompt=consent. - 회원이 해지하면 그 회원의 토큰이 모두 끊깁니다(
401 invalid_token) — 다시 쓰려면 동의를 새로 받습니다. 해지 길은 앱 등록의 회원 해지 값을 따릅니다(마이페이지 > 연결된 앱 [해지] 또는 쇼핑몰 고객센터). 판매자는 관리자 > 연결된 앱에서 앱 단위로 끊습니다. - 그 쇼핑몰에 등록된 앱만 받습니다. 연결 도메인으로 열어도 됩니다.
- 토큰 교환은 같은
https://api.mallstory.kr/api/oauth/token입니다. 브라우저 · 모바일 앱은client_secret없이client_id만 보냅니다(보내면 거절됩니다). - 토큰 응답에
"subject": "member"와member_unique가 함께 옵니다.
bash브라우저 · 모바일 앱의 토큰 교환 (비밀 키 없음)
curl -X POST 'https://api.mallstory.kr/api/oauth/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d grant_type=authorization_code \
-d code=SplxlOBeZQQYbYS6WxSbIA \
-d redirect_uri=https://shop.example.com/oauth/callback \
-d client_id=ms_xxxxxxxxxxxx \
-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk회원 토큰으로 할 수 있는 것
회원 토큰은 그 회원 한 명만 다룹니다 — /v1/me 만 부를 수 있고, 쇼핑몰 관리 API · MCP 는 403 입니다. 반대로 소유자 토큰으로 /v1/me 를 부르면 403 입니다.