Developers
제휴사 연동

ⓐ 자격증명 로그인 API

쏠브가 회원의 ID/PW 를 보내면 확인하고 user_key 를 돌려주는, 제휴사가 구현하는 API 입니다.

회원이 쏠브 앱에 입력한 ID/PW 를 쏠브 서버가 그대로 보내면, 제휴사가 확인한 뒤 user_key 를 돌려주는 server-to-server API 입니다. 자체 ID/PW 로그인만 운영하는 제휴사에 권장합니다. 소셜 로그인을 함께 운영한다면 ⓑ 호스팅 로그인 페이지를 쓰세요.

한눈에 보기

항목내용
호출하는 쪽쏠브 서버 → 제휴사 서버
메서드·URLPOST, URL 은 제휴사가 정합니다
형식Content-Type: application/json 요청, JSON 응답
인증제휴사가 발급한 시크릿을 쏠브가 헤더에 담습니다. 헤더 이름과 방식은 제휴사가 정합니다.
타임아웃호출당 10초
재시도5xx·타임아웃이면 최대 2번 더 호출합니다. 4xx 는 재시도하지 않습니다.

요청

쏠브가 보내는 요청은 아래와 같습니다. 헤더 이름 X-Solve-Secret 은 예시입니다.

쏠브가 보내는 요청 (curl 로 재현)
curl -X POST https://api.partner.example.com/solve/login \
  -H 'Content-Type: application/json' \
  -H 'X-Solve-Secret: <제휴사가 발급한 시크릿>' \
  -d '{"id": "partner_user_01", "password": "p@ssw0rd"}'
필드타입필수설명
idstring필수회원이 입력한 로그인 아이디
passwordstring필수회원이 입력한 비밀번호

응답

성공 (200)

{ "user_key": "100023" }
필드타입필수설명
user_keystring필수회원의 불변·유일 식별자, 최대 255자. 내부 회원번호를 권장합니다. 다른 필드를 더 담아도 쏠브는 무시합니다.

응답 본문은 1MB(1,048,576바이트) 이하여야 합니다. 쏠브는 1MB 를 넘는 응답을 받지 않습니다. 넘으면 그 로그인 호출은 실패로 처리하고 재시도하지 않습니다. 압축해서 보내는 경우는 푼 뒤의 크기로 셉니다.

실패

쏠브는 상태 코드로 인증 실패와 시스템 오류를 구분해 회원에게 다른 안내를 보여줍니다. 본문 형식은 자유입니다.

상황상태 코드쏠브 앱에서 보이는 안내재시도
아이디·비밀번호 불일치401 등 4xx"아이디 또는 비밀번호가 일치하지 않습니다"안 함
시크릿 불일치401 또는 403위와 같음안 함
요청 형식 오류400 등 4xx위와 같음안 함
서버 오류, DB 장애500 등 5xx일반 오류 안내최대 2번

쏠브는 401 과 403 을 구분하지 않고 같은 인증 실패로 처리합니다. 둘 다 재시도하지 않습니다.

서버 오류를 4xx 로 보내지 마세요

장애 상황을 4xx 로 응답하면 회원은 비밀번호가 틀린 줄 알고 계속 다시 입력합니다. 예외가 나면 반드시 5xx 로 응답해 주세요.

구현 예시

시크릿 확인 → 입력값 확인 → 회원 인증 → user_key 응답 순서입니다. 회원 조회·비밀번호 검증 함수는 제휴사 시스템에 맞게 바꾸세요.

import express from 'express';
import crypto from 'node:crypto';

const app = express();
app.use(express.json());

// 제휴사가 발급해 쏠브에 전달한 시크릿
const SOLVE_SECRET = Buffer.from(process.env.SOLVE_SECRET ?? '');
// 시크릿이 비어 있으면 빈 헤더 요청이 통과하므로 서버를 띄우지 않는다
if (SOLVE_SECRET.length === 0) throw new Error('SOLVE_SECRET is not set');

function isFromSolve(req) {
  const given = Buffer.from(req.get('X-Solve-Secret') ?? '');
  return given.length === SOLVE_SECRET.length && crypto.timingSafeEqual(given, SOLVE_SECRET);
}

app.post('/solve/login', async (req, res) => {
  if (!isFromSolve(req)) return res.status(403).json({ error: 'forbidden' });

  const { id, password } = req.body ?? {};
  if (typeof id !== 'string' || typeof password !== 'string') {
    return res.status(400).json({ error: 'invalid_request' });
  }

  try {
    const member = await findMemberByLoginId(id);
    if (!member || !(await verifyPassword(password, member.passwordHash))) {
      return res.status(401).json({ error: 'invalid_credentials' });
    }
    // 로그인 아이디가 아니라 절대 바뀌지 않는 내부 회원번호
    return res.json({ user_key: String(member.memberNo) });
  } catch (err) {
    console.error('solve login failed', err);
    return res.status(500).json({ error: 'internal_error' });
  }
});

흐름

회원

쏠브 앱

제휴사

사이트·서버

쏠브

서버

1

회원이 제휴사 사이트에서 책을 삽니다. 여기까지는 지금과 같습니다.

  1. 회원 → 제휴사: 책 구매. 회원이 제휴사 사이트에서 책을 삽니다. 여기까지는 지금과 같습니다.
  2. 회원 → 쏠브: 아이디·비밀번호 입력. 쏠브 앱에서 "제휴사 연동하기"를 누르고 제휴사 아이디와 비밀번호를 입력합니다.
  3. 쏠브 → 제휴사: "이 아이디 맞나요?". 쏠브 서버가 제휴사에 아이디와 비밀번호가 맞는지 물어봅니다.
  4. 제휴사 → 쏠브: "100023번 회원이에요". 맞으면 제휴사가 회원 번호로 답합니다. 틀리면 "비밀번호 불일치"로 답합니다.
  5. 쏠브 → 제휴사: "이 회원 뭐 샀어요?". 쏠브가 제휴사에 이 회원이 지금 볼 수 있는 상품을 물어봅니다.
  6. 제휴사 → 쏠브: 상품코드 목록. 제휴사는 구매한 상품의 코드 목록으로 답합니다. 환불된 상품은 빼고 보냅니다.
  7. 쏠브 → 회원: 책장에 책 추가. 쏠브가 상품코드로 전자책을 찾아 회원의 책장에 넣습니다. 상품코드는 파트너 어드민에 미리 등록해 둡니다.

구현 체크리스트

  • 로그인에 성공하면 내부 회원번호처럼 바뀌지 않는 값을 user_key 로 돌려준다
  • 비밀번호 불일치는 4xx, 서버 오류는 5xx 로 응답한다
  • 응답 본문을 1MB(1,048,576바이트) 이하로 만든다
  • 시크릿을 상수 시간 비교(timingSafeEqual, hash_equals 등)로 확인한다
  • ID/PW 를 로그에 남기지 않는다
  • 같은 요청을 여러 번 받아도 데이터가 바뀌지 않는다 (쏠브가 재시도할 수 있음)

체크리스트 항목 중 응답으로 확인할 수 있는 것(맞는·틀린 비밀번호, 틀린 시크릿, user_key 형식, 응답 시간)은 전체 점검 도구로 한 번에 점검할 수 있습니다.

관련 문서

이 페이지 목차