개요
놀아:회원은 자체 회원가입과 외부 서비스용 OAuth 로그인을 함께 제공하는 서버입니다.
외부 서비스는 사용자의 이메일이 아닌, 서비스별 고유 식별자/닉네임/프로필 사진 URL만 받습니다.
- 외부 서비스는 사전 도메인 인증이 필요합니다.
- 첫 로그인에는 사용자 동의가 필요합니다.
state와 PKCE(S256)를 사용합니다.
- authorization code는 1회용이며 10분 유효합니다.
sub는 서비스별로 달라지는 고유 식별자입니다.
엔드포인트
GET /oauth/authorize
Authorization 요청 진입점
POST /oauth/consent
동의/거부 처리
POST /oauth/token
authorization code / refresh token 교환
GET /oauth/userinfo
사용자 프로필 조회
1. Authorization 요청
외부 서비스는 사용자를 다음 형태의 URL로 이동시켜야 합니다.
GET https://account.nola.kr/oauth/authorize
?response_type=code
&client_id=CLIENT_ID
&redirect_uri=https://service.example.com/oauth/callback
&scope=profile.basic profile.avatar
&state=RANDOM_STATE
&code_challenge=BASE64URL_SHA256_CODE_VERIFIER
&code_challenge_method=S256
필수 조건
response_type는 반드시 code여야 합니다.
redirect_uri는 등록된 callback URL과 정확히 일치해야 합니다.
- 서버는 등록된 callback URL을 기준으로 처리합니다. 요청값이 다르면
invalid_redirect_uri로 실패합니다.
scope는 공백으로 구분합니다. 콤마는 표준 구분자가 아닙니다.
state는 CSRF 방지와 응답 매칭을 위해 매 요청마다 랜덤해야 합니다.
code_challenge_method는 S256만 허용됩니다.
2. 샘플 요청/응답
외부 서비스 개발자가 바로 붙일 수 있도록, 성공/실패 예시를 함께 제공합니다.
Authorization 요청 예시
GET https://account.nola.kr/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https%3A%2F%2Fservice.example.com%2Foauth%2Fcallback&scope=profile.basic%20profile.avatar&state=RANDOM_STATE&code_challenge=BASE64URL_SHA256_CODE_VERIFIER&code_challenge_method=S256
Authorization 성공 후 callback 예시
GET https://service.example.com/oauth/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE
redirect_uri 불일치 실패 예시
GET https://service.example.com/oauth/callback?error=invalid_redirect_uri&error_description=redirect_uri%20does%20not%20match%20registered%20callback%20URL&state=RANDOM_STATE
Token 요청 예시
POST https://account.nola.kr/oauth/token
grant_type=authorization_code
client_id=CLIENT_ID
client_secret=CLIENT_SECRET
code=AUTHORIZATION_CODE
redirect_uri=https://service.example.com/oauth/callback
code_verifier=ORIGINAL_CODE_VERIFIER
Token 응답 예시
{
"access_token": "....",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "....",
"scope": "profile.basic profile.avatar"
}
UserInfo 요청/응답 예시
GET https://account.nola.kr/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
{
"sub": "pws_2G6O...서비스별고유값...",
"nickname": "nola",
"profile_image_url": "https://example.com/account/public/uploads/profile/nola.jpg",
"scope": "profile.basic profile.avatar"
}
실제 응답의 profile_image_url은 등록된 프로필 이미지가 없으면 null일 수 있습니다.
3. 동의 화면
사용자가 해당 서비스에 처음 로그인하면 동의 화면을 보여줍니다.
화면에는 서비스 이름, 서비스 설명, 요청 scope, 전달되는 정보가 표시됩니다.
- 전달 정보: 닉네임, 프로필 사진 URL
- 전달하지 않는 정보: 이메일, 비밀번호
- 사용자는 동의 또는 거부를 선택할 수 있습니다.
4. Token 교환
동의 후 반환된 code는 1회만 사용할 수 있습니다. 외부 서비스 서버는 백엔드에서 토큰 엔드포인트를 호출합니다.
토큰 요청의 redirect_uri도 반드시 등록된 callback URL과 같아야 합니다. 다르면 invalid_redirect_uri로 실패합니다.
POST https://account.nola.kr/oauth/token
grant_type=authorization_code
client_id=CLIENT_ID
client_secret=CLIENT_SECRET
code=AUTHORIZATION_CODE
redirect_uri=https://service.example.com/oauth/callback
code_verifier=ORIGINAL_CODE_VERIFIER
응답 예시
{
"access_token": "....",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "....",
"scope": "profile.basic profile.avatar"
}
5. UserInfo
access token으로 사용자 정보를 조회합니다.
GET https://account.nola.kr/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
응답 필드
sub - 서비스별 고유 식별자
nickname - 닉네임
profile_image_url - scope가 있으면 포함
scope - 승인된 scope 문자열
Scope
profile.basic
서비스별 고유 식별자와 닉네임을 제공합니다.
profile.avatar
서비스별 고유 식별자, 닉네임, 프로필 사진 URL을 제공합니다.
scope는 필요한 최소 범위만 요청해야 하며, 사용자가 동의하지 않은 범위는 발급되지 않습니다.
PKCE
놀아:회원은 PKCE(S256)를 필수로 요구합니다.
code_verifier는 외부 서비스가 생성합니다.
code_challenge = base64url(SHA256(code_verifier))
- 토큰 요청 시 원본
code_verifier를 다시 보내 검증합니다.
상태값
draft초안
queued검증 대기
pending_callback콜백 대기
active사용 가능
revoked차단됨