oauth
oauth 카테고리의 API 작업 7개를 다룹니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /.well-known/oauth-authorization-server | 인가 서버 메타데이터 (RFC 8414) |
| GET | /.well-known/jwks.json | 액세스 토큰의 공개 키 |
| POST | /v1/oauth/authorize | 연결된 앱을 승인하고 인가 코드 발급 |
| POST | /oauth/token | 코드 교환 또는 갱신 (RFC 6749) |
| POST | /oauth/revoke | 연결된 앱 로그아웃 (RFC 7009) |
| GET | /v1/oauth/grants | 연결된 앱 |
| DELETE | /v1/oauth/grants/{sid} | 연결된 앱 제거 |
GET /.well-known/oauth-authorization-server
클라이언트가 자격 증명을 갖기 전에 읽는 문서이므로 정의상 공개되어 있습니다.
authorization_endpoint는 이 API가 아니라 대시보드를 가리킵니다. 인가 단계는 브라우저에 있는 사용자의 로그인 세션을 볼 수 있어야 하기 때문입니다 (docs/studio.md §8.1). runlot_access_token_audience는 RFC 8414에 없습니다. 리소스 서버가 따로 설정된 값(발급자와 어긋날 수 있습니다) 대신 발급자로부터 반드시 요구해야 할 aud를 알 수 있도록 넣었습니다.
operationId oauthMetadata
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 메타데이터 문서 | object |
| 503 | 이 배포에는 연결된 앱 로그인이 설정되어 있지 않습니다 | Error |
GET /.well-known/jwks.json
폐기되지 않은 모든 서명 키입니다. 키 교체 중에는 두 개가 게시됩니다. 이것이 집합으로 제공되는 이유입니다. 키 하나만 캐시한 검증자는 다른 키로 발급된 토큰을 거절하게 됩니다. 캐시는 1분까지만 가능합니다. 오래된 사본은 새 토큰을 거절합니다.
operationId oauthJwks
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 키 집합 | object |
POST /v1/oauth/authorize
대시보드의 /oauth/authorize 화면이 사용자의 로그인 세션으로 호출합니다. 개인 액세스 토큰은 스코프와 관계없이 거절됩니다. 자격 증명이 스스로를 수명이 더 긴 자격 증명으로 넓힐 수 있어서는 안 되기 때문입니다.
화면이 전달받은 쿼리 문자열을 그대로 넘기므로, 파라미터는 이 문서의 camelCase가 아니라 OAuth 고유의 이름을 사용합니다.
operationId oauthAuthorize
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 승인되었습니다. 브라우저가 redirect로 이동합니다. | object |
| 400 | — | — |
| 401 | — | — |
POST /oauth/token
JSON이 아니라 폼 인코딩입니다. RFC 6749 §4.1.3에 따르며 모든 클라이언트 라이브러리가 이 방식으로 보냅니다.
실패 응답은 이 문서의 오류 봉투가 아니라 RFC 6749 §5.2의 본문입니다. 이 두 엔드포인트는 우리 형식을 해석하지 못하는 일반 OAuth 클라이언트가 읽기 때문입니다.
리프레시 토큰은 교체됩니다. 하나를 사용하면 그 토큰은 무효가 되고 다음 토큰이 반환됩니다. 클라이언트별, 주소별로 요청 횟수가 제한됩니다 (429).
operationId oauthToken
요청 본문: application/x-www-form-urlencoded · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 새 액세스 토큰과 다음 리프레시 토큰 | OAuthTokenResponse |
| 400 | RFC 6749 §5.2 | OAuthError |
| 429 | 이 클라이언트 또는 이 주소에서 시도가 너무 많습니다 | OAuthError |
| 503 | 이 배포에는 연결된 앱 로그인이 설정되어 있지 않습니다 | OAuthError |
POST /oauth/revoke
폼 인코딩입니다. 모르는 토큰에도 200으로 응답합니다. RFC 7009 §2.2가 요구하는 동작이며, 다른 응답을 하면 이 엔드포인트가 어떤 토큰이 존재하는지 알려 주는 오라클이 되기 때문입니다.
리프레시 토큰을 폐기하면 교체 세대 하나가 아니라 grant 전체가 폐기됩니다. "Studio에서 로그아웃"하면 해당 기기가 연결된 앱에서도 사라진다는 뜻입니다 (docs/studio.md §8.4). 클라이언트별, 주소별로 요청 횟수가 제한됩니다 (429).
operationId oauthRevoke
요청 본문: application/x-www-form-urlencoded · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 폐기되었거나 모르는 토큰입니다. 두 경우는 의도적으로 같은 응답입니다 | — |
| 400 | RFC 6749 §5.2 | OAuthError |
| 429 | 이 클라이언트 또는 이 주소에서 시도가 너무 많습니다 | OAuthError |
GET /v1/oauth/grants
지금 이 계정에 접근할 수 있는 대상입니다 (docs/studio.md §8.4). 폐기된 grant는 나열하지 않습니다. 죽은 행을 남겨 두는 목록은 읽는 쪽이 직접 걸러 내야 하는 답이 되기 때문입니다.
POST /v1/oauth/authorize와 같은 이유로 세션만 허용합니다.
operationId listOAuthGrants
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | grant 목록 | object |
| 401 | — | — |
DELETE /v1/oauth/grants/{sid}
리프레시 토큰은 함께 무효가 되고, 액세스 토큰은 sid 확인을 거치는 다음 요청에서 무효가 됩니다. 폐기는 cp-core의 피드를 통해 Studio 서비스에 전달되므로, 빌려 준 모델은 액세스 토큰의 15분을 기다리지 않고 즉시 끊깁니다.
이 사용자의 것이 아닌 grant는 403이 아니라 404입니다. 다른 사람의 grant id가 존재한다고 호출자에게 알려 주면 grant id를 탐색하는 통로가 되기 때문입니다.
operationId revokeOAuthGrant
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 204 | 제거됨 | — |
| 401 | — | — |
| 404 | — | — |