본문으로 건너뛰기
페이싱크 개발자센터
Esc
↑↓이동↵열기⌘J미리보기
이 페이지에서

인가 요청

GET https://paysync.kr/oauth/authorize 로 고객을 페이싱크 연결 화면에 보내는 인가 요청의 쿼리 파라미터, PKCE, 리디렉션으로 돌아오는 인가 코드와 오류를 설명해요.

GET https://paysync.kr/oauth/authorize

고객의 브라우저를 페이싱크 연결 화면으로 보내요. API 서버(api.paysync.kr)가 아니라 대시보드 주소(paysync.kr)예요. 고객이 로그인하고 워크스페이스를 고른 뒤 허용하면 redirect_uri로 인가 코드가 돌아와요. 고객이 보는 화면은 연결된 앱을 참고하세요.

쿼리 파라미터

파라미터 타입 필수 설명
response_type string 예 code로 보내요.
client_id string 예 앱 ID(oap_...)예요.
redirect_uri string 예 인가 코드를 받을 주소예요. 앱에 등록한 값과 똑같아야 해요.
scope string 예 요청할 스코프를 공백으로 구분해 넣어요. 예: invoices:read transactions:read. 앱에 등록된 스코프만 요청할 수 있어요.
state string 인가 코드와 함께 그대로 돌려줘요. 요청마다 추측할 수 없는 값을 만들어 세션에 저장하고, 돌아왔을 때 같은지 확인하세요.
code_challenge string PKCE를 쓸 때 넣어요. code_verifier를 SHA-256으로 해시하고 패딩 없는 base64url로 인코딩한 43자예요. PKCE 참고
code_challenge_method string S256만 지원해요. code_challenge를 보낼 때 꼭 넣어요.

쿼리 값은 URL 인코딩해요. scope의 공백은 %20이나 +로 보내요.

요청 예시

https://paysync.kr/oauth/authorize?response_type=code&client_id=oap_a1b2c3d4e5f6g7h8i9j0k1l2&redirect_uri=https%3A%2F%2Fpartner.example.com%2Fpaysync%2Fcallback&scope=invoices%3Aread%20transactions%3Aread&state=Zx81kQ0aLp&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256
import { createHash, randomBytes } from "node:crypto";

const state = randomBytes(16).toString("base64url");
const codeVerifier = randomBytes(32).toString("base64url");
const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
// state와 codeVerifier는 고객 세션에 저장해 두세요.

const url = new URL("https://paysync.kr/oauth/authorize");
url.search = new URLSearchParams({
  response_type: "code",
  client_id: process.env.PAYSYNC_CLIENT_ID,
  redirect_uri: "https://partner.example.com/paysync/callback",
  scope: "invoices:read transactions:read",
  state,
  code_challenge: codeChallenge,
  code_challenge_method: "S256",
}).toString();

res.redirect(url.toString());
import base64
import hashlib
import os
import secrets
from urllib.parse import urlencode

state = secrets.token_urlsafe(16)
code_verifier = secrets.token_urlsafe(32)
code_challenge = (
    base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest())
    .rstrip(b"=")
    .decode()
)
# state와 code_verifier는 고객 세션에 저장해 두세요.

url = "https://paysync.kr/oauth/authorize?" + urlencode(
    {
        "response_type": "code",
        "client_id": os.environ["PAYSYNC_CLIENT_ID"],
        "redirect_uri": "https://partner.example.com/paysync/callback",
        "scope": "invoices:read transactions:read",
        "state": state,
        "code_challenge": code_challenge,
        "code_challenge_method": "S256",
    }
)
# 고객의 브라우저를 url로 리디렉션해요.

응답

연결 화면은 JSON을 돌려주지 않아요. 고객의 브라우저를 redirect_uri로 이동시켜요. redirect_uri에 원래 쿼리가 있으면 그대로 두고 뒤에 붙여요.

고객이 허용했을 때

code를 붙여 이동해요. state를 보냈다면 state도 붙여요.

https://partner.example.com/paysync/callback?code=k3jq8qxvb7rt2mlw9syda1b2c3d4e5f6g7h8i9j0&state=Zx81kQ0aLp
파라미터 설명
code 인가 코드예요. 고객이 고른 워크스페이스와 허용한 스코프가 담겨 있어요.
state 인가 요청에 보낸 state예요.

인가 코드는 10분 동안 한 번만 쓸 수 있어요. 받은 즉시 토큰 발급으로 바꾸세요.

고객이 취소했을 때

연결 화면에서 취소를 누르면 error=access_denied를 붙여 이동해요. state를 보냈다면 state도 붙여요.

https://partner.example.com/paysync/callback?error=access_denied&state=Zx81kQ0aLp

오류

아래 경우에는 redirect_uri로 돌아가지 않고 연결 화면에 오류를 보여 줘요. 잘못된 주소로 코드가 새지 않게 하려는 동작이에요.

연결 화면의 안내 발생 조건
잘못된 연결 요청이에요 response_type이 code가 아니거나, client_id나 redirect_uri가 없거나, scope가 비었거나 모르는 값이 있어요.
등록되지 않은 앱이에요 client_id에 맞는 앱이 없어요.
앱에 등록되지 않은 돌아갈 주소예요 redirect_uri가 앱에 등록되지 않았어요.
앱에 허용되지 않은 권한을 요청했어요 앱에 등록되지 않은 스코프를 요청했어요.
앱의 연결 요청이 올바르지 않아요 code_challenge가 43자 base64url이 아니거나 code_challenge_method가 S256이 아니에요. 고객이 연결 허용을 누를 때 떠요.

PKCE

PKCE를 쓰면 인가 코드가 중간에 새도 code_verifier 없이는 토큰으로 바꿀 수 없어요. 클라이언트 시크릿을 보관하는 서버에서도 함께 쓰는 것을 권장해요.

  1. 요청마다 A-Z, a-z, 0-9, -, ., _, ~로 된 43~128자 code_verifier를 만들어 세션에 저장해요.
  2. code_challenge는 BASE64URL(SHA256(code_verifier))이고, 끝의 =는 빼요.
  3. 인가 요청에 code_challenge와 code_challenge_method=S256을 넣어요.
  4. 토큰 요청에 같은 code_verifier를 넣어요.

32바이트 난수를 base64url로 인코딩하면 43자 code_verifier가 돼요. 위 요청 예시의 Node.js와 Python 코드가 이 방식이에요.

이 페이지가 도움이 되었나요?