회원 로그인
쇼핑몰이 직접 만든 화면 · 앱이 자기 로그인 폼의 아이디 · 비밀번호로 회원 토큰을 바로 받습니다. 동의 화면이 없습니다.
업데이트됨 10.13 — 회원 로그인에 회원 IP · User-Agent 를 넘길 수 있습니다언제 쓰나
쇼핑몰이 직접 만든 화면(헤드리스 스토어프론트 · 자체 모바일 앱)이 자기 로그인 폼으로 회원을 로그인시킬 때 씁니다. 회원이 넣은 아이디 · 비밀번호를 앱의 서버가 넘기면 회원 토큰이 바로 나옵니다 — 동의 화면 · 쇼핑몰 주소로 넘어가는 일이 없습니다.
text
회원 → 앱의 로그인 폼에 아이디 · 비밀번호
앱 서버 → POST https://api.mallstory.kr/v1/member/login (client_id · client_secret · login_id · password)
← access_token · refresh_token · subject=member · member_unique
앱 서버 → GET https://api.mallstory.kr/v1/me (Authorization: Bearer access_token)| 회원 로그인 | 회원 동의 | |
|---|---|---|
| 쓰는 곳 | 쇼핑몰 자신의 화면 · 앱 | 남의 서비스 |
| 비밀번호를 받는 곳 | 그 앱의 로그인 폼(앱 서버) | 쇼핑몰 동의 화면만 |
| 동의 화면 | 없음 | 있음 |
| 앱 종류 | 서버 앱(비밀 키) | 서버 앱 · 브라우저 · 모바일 앱 |
남의 서비스에는 쓰지 않습니다
이 방식은 앱이 회원 비밀번호를 직접 받습니다. 쇼핑몰 운영자가 만든 앱에만 쓰고, 다른 회사의 서비스가 회원 정보를 쓰려면 회원 동의를 쓰게 하세요.
앱 준비
- 앱 종류는 서버 앱입니다. 비밀 키가 실리므로 반드시 서버에서 부릅니다. 브라우저 · 모바일 앱(비밀 키 없음)은 거절됩니다(
unauthorized_client). - 권한에 회원 본인 권한(
me:read·me:write)을 켭니다. 관리 권한과 한 앱에 함께 켜도 됩니다 — 이 요청으로 나가는 토큰에는 회원 권한만 실립니다. - 앱을 등록한 그 쇼핑몰의 회원만 로그인할 수 있습니다.
요청
POST
https://api.mallstory.kr/v1/member/login본문은 application/json 또는 application/x-www-form-urlencoded 입니다.
| 이름 | 형식 | 설명 |
|---|---|---|
client_id필수 | string | Client ID |
client_secret필수 | string | 비밀 키. Authorization: Basic 헤더로 보내도 됩니다 |
login_id필수 | string | 회원이 넣은 아이디 |
password필수 | string | 회원이 넣은 비밀번호 |
scope | string | 공백으로 나눈 회원 권한. 없으면 앱의 회원 권한 전부(예: me:read) |
bash
curl -X POST 'https://api.mallstory.kr/v1/member/login' -H 'Content-Type: application/json' -d '{
"client_id": "ms_xxxxxxxxxxxx",
"client_secret": "mss_xxxxxxxxxxxx",
"login_id": "hong",
"password": "회원이 넣은 비밀번호"
}'회원 IP 넘기기
이 API 는 앱 서버가 부르므로, 그냥 두면 쇼핑몰의 회원 로그인 기록(쇼핑몰 관리자 > 회원 > 로그인 기록)에 회원 기기가 아니라 앱 서버의 IP · 단말이 남습니다. 낯선 곳에서의 로그인 · 무차별 대입을 가리려면 회원의 값을 헤더로 넘기세요.
| 요청 헤더 | 형식 | 설명 |
|---|---|---|
X-Member-IP | string | 회원 기기의 IP 하나(IPv4 · IPv6). 앱 서버가 회원 브라우저에서 받은 요청의 접속 IP 를 넣습니다. 쉼표 목록 · 포트를 넣으면 400 invalid_request |
X-Member-User-Agent | string | 회원 브라우저 · 앱의 User-Agent 그대로(512자까지). 기록의 운영체제 · 기기 · 브라우저가 이 값으로 정해집니다 |
// Express — 회원 브라우저가 앱 서버의 /login 으로 아이디 · 비밀번호를 보냈을 때
// 앱 서버가 프록시(nginx · 로드밸런서) 뒤라면 app.set("trust proxy", 1) 을 켜야 req.ip 가 회원 IP 입니다.
app.post("/login", async (req, res) => {
const response = await fetch("https://api.mallstory.kr/v1/member/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Member-IP": req.ip, // 회원 기기 IP
"X-Member-User-Agent": req.get("user-agent") ?? "", // 회원 브라우저
},
body: JSON.stringify({
client_id: process.env.MALLSTORY_CLIENT_ID,
client_secret: process.env.MALLSTORY_CLIENT_SECRET,
login_id: req.body.login_id,
password: req.body.password,
}),
});
// … 토큰은 서버 세션에 두고 비밀번호는 버린다
});- 회원 IP 는 앱 서버가 직접 받은 연결에서 꺼냅니다. 회원 브라우저가 보낸
X-Forwarded-For를 그대로 옮기면 회원이 아무 IP 나 적을 수 있습니다 — 앱 서버 앞의 내 프록시가 넣은 값(또는 소켓의 접속 IP)만 씁니다. - 이 헤더는 비밀 키로 앱을 확인한 뒤에만 읽습니다(서버 앱만 넘길 수 있음).
X-Forwarded-For는 쇼핑몰 앞단이 덧붙이는 칸이라 회원 IP 로 쓰지 않습니다 — 반드시X-Member-IP로 보냅니다. - 기록에는 넘긴 회원 IP 와 함께 실제로 부른 앱 서버의 IP · 앱(Client ID)이 따로 남습니다. 관리자는 로그인 기록 상세에서 '경유 앱 · 앱 서버 IP' 를 봅니다.
- 넘기지 않아도 로그인은 됩니다 — 그때 기록의 IP 는 앱 서버의 것이고, 상세에 '회원 IP 를 넘기지 않음' 으로 보입니다.
응답
토큰 발급과 같은 모양입니다. 갱신 · 폐기도 같은 엔드포인트(/api/oauth/token · /api/oauth/revoke)를 씁니다.
json200 OK
{
"subject": "member",
"member_unique": "6accb17807e52ce89536610b",
"access_token": "msat_…",
"refresh_token": "msrt_…",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "me:read me:write",
"shop_unique": "6a9515bdcb3ab5179ba767ad"
}json400 — 비밀번호가 틀림
{
"error": "invalid_grant",
"error_description": "아이디 또는 비밀번호가 올바르지 않습니다."
}| 상태 | error | 언제 |
|---|---|---|
| 400 | invalid_grant | 아이디 또는 비밀번호가 틀림(둘을 나누어 알려 주지 않습니다) · 정지 · 탈퇴 · 연속 실패로 잠김 · 소셜 로그인 전용 회원 |
| 400 | invalid_scope | 앱에 회원 권한이 없음 · 회원 권한이 아닌 scope 를 요청함 |
| 400 | unauthorized_client | 브라우저 · 모바일 앱(비밀 키 없음)으로 부름 |
| 400 | invalid_request | login_id · password 가 없음 · X-Member-IP 가 IP 모양이 아님 |
| 401 | invalid_client | Client ID · 비밀 키가 틀렸거나 앱이 중지됨 |
error_description 에 회원에게 보여 줄 수 있는 문구가 실립니다(예: "로그인에 여러 번 실패해 잠시 잠긴 계정입니다.").
지킬 것
- 비밀번호를 저장하지 않습니다. 받은 즉시 이 API 로 넘기고 버립니다. 이후로는 토큰만 씁니다.
- 토큰은 서버(HttpOnly 쿠키 · 서버 세션)에 둡니다. 브라우저 자바스크립트가 읽는 곳에 두지 않습니다.
- 쇼핑몰 로그인과 같은 규칙이 걸립니다 — 연속 실패 잠금 · 정지 · 탈퇴 · 접속 기록. 잠긴 회원은 잠시 뒤 다시 시도해야 합니다. 접속 기록에 회원 IP 가 남게 X-Member-IP 를 넘기세요.
- 소셜 로그인으로만 가입한 회원은 비밀번호가 없어 이 방식으로 로그인할 수 없습니다. 그 회원은 회원 동의를 씁니다.
- 로그인한 회원은 쇼핑몰 관리자 > 설정 > 외부연동 설정 > 연결된 앱에 앱별 '회원 N명' 으로 보이고, 거기서 해지할 수 있습니다.