Foundry OAuth 2.0 클라이언트의 승인과 토큰
OAuth 2.0은 외부 앱이 사용자의 비밀번호나 고정 개인 토큰을 받는 대신, 정해진 승인 방식으로 access token을 발급받아 서비스에 접근하는 체계입니다. 여기서 클라이언트는 외부 앱, 승인 서버는 Foundry, 사용자는 그 앱을 사용하는 Foundry 계정 소유자입니다.
먼저 앱을 등록하고 사용할 조직에서 활성화해야 합니다. 사용자의 업무를 대신 수행하는 앱은 Authorization code grant를, 일반 사용자와 연결되지 않는 비대화형 서비스 작업은 Client credentials grant를 사용합니다.
Authorization code grant
이 방식에서는 기존 Foundry 사용자가 앱에 요청한 권한을 위임합니다. 앱이 요청한 OAuth scope, 사용자의 권한과 조직별 앱 접근 제한이 함께 적용됩니다.
PKCE와 state 준비
PKCE를 사용할 때에는 승인 요청마다 code_verifier와 code_challenge를 만듭니다. Public client에는 필수이며, secret을 안전하게 저장할 수 있는 Confidential client에도 권장됩니다.
code_verifier는 암호학적으로 무작위인 43~128자 문자열입니다. 허용 문자는A-Z,a-z,0-9,-,.,_,~입니다.code_challenge는 verifier의 SHA-256 결과를 패딩 없는 Base64 URL 형식으로 인코딩한 값입니다.code_challenge_method는S256입니다.
앱은 CSRF 방지를 위한 무작위 state도 생성하고 승인 요청과 연결해 보관합니다. 돌아온 state가 원래 값과 일치하는지 확인해야 합니다. Verifier는 브라우저 승인 URL에 보내지 않고 나중에 토큰 교환에서 사용합니다.
브라우저 승인 요청
사용자의 브라우저를 다음 엔드포인트로 이동시킵니다.
GET /multipass/api/oauth2/authorize
| 쿼리 파라미터 | 형식 | 설정 |
|---|---|---|
response_type | String | code로 지정합니다. |
client_id | String | 앱 등록 시 발급한 ID입니다. |
redirect_uri | String | 등록한 redirect URI 중 하나와 일치하는 절대 URI입니다. 생략하면 앱 설정의 첫 URI를 사용합니다. |
scope | String | 요청할 권한 범위를 공백으로 구분합니다. API별 필요한 scope를 선택하며, refresh token이 필요하면 offline_access를 포함합니다. |
state | String, 선택 | 앱이 생성하고 반환값을 검증할 임의 문자열입니다. 안전한 클라이언트에서는 사용하고 검증합니다. |
code_challenge | String, 조건부 | PKCE를 사용하면 지정합니다. Public client에서는 필수입니다. |
code_challenge_method | String, 조건부 | challenge를 보낼 때 S256을 지정합니다. |
Foundry의 승인 화면에는 요청한 접근 범위가 표시됩니다. 사용자가 승인하면 등록된 목적지로 돌아갑니다.
성공한 리다이렉트에는 다음 쿼리 파라미터가 포함됩니다.
| 파라미터 | 형식 | 의미 |
|---|---|---|
code | String | 승인 서버가 발급한 authorization code입니다. 10분 후 만료됩니다. |
state | String, 선택 | 요청에 state가 있었다면 정확히 같은 값이 반환됩니다. |
앱은 state를 검증한 뒤 code를 access token으로 교환합니다. 승인 code를 데이터 API의 bearer token처럼 사용하지 않습니다.
Code를 토큰으로 교환
POST /multipass/api/oauth2/token에 application/x-www-form-urlencoded 본문을 보냅니다. grant_type=authorization_code, 받은 code, 등록된 client_id를 지정합니다. 승인 요청에서 redirect_uri를 보냈다면 교환에서도 같은 redirect URI를 보내야 합니다. PKCE를 사용했다면 처음 생성한 code_verifier도 보냅니다.
승인 서버는 verifier와 최초 challenge의 일치를 확인하고 access token을 발급합니다. 요청 scope에 offline_access를 포함했다면 refresh token도 받을 수 있습니다.
Client credentials grant
이 방식은 사람을 대신하지 않는 서비스 작업을 위한 것이며 Confidential client만 사용할 수 있습니다. 앱에 연결된 Foundry 서비스 사용자가 생성되고, 그 사용자 이름은 앱의 client ID와 같습니다.
서비스 사용자는 기본적으로 어떤 리소스에도 접근할 수 없습니다. 관리자가 작업에 필요한 역할과 권한을 부여해야 합니다.
토큰 엔드포인트에 다음 폼 필드를 보냅니다.
| 필드 | 값 |
|---|---|
grant_type | client_credentials |
client_id | 등록한 앱의 ID |
client_secret | 등록 또는 교체 시 발급한 앱의 secret |
응답의 access_token, token_type, expires_in을 사용해 서비스 사용자 권한으로 API를 호출합니다. 이 방식의 토큰을 일반 사용자의 승인으로 해석하지 않습니다.
Access token 갱신
사용자가 자리에 없을 때도 접근이 필요한 Authorization code 클라이언트는 처음 승인할 때 offline_access를 요청하고 응답의 refresh token을 안전하게 보관합니다.
Access token이 만료되면 토큰 엔드포인트에 grant_type=refresh_token, 마지막으로 저장한 refresh_token, client_id를 보냅니다. Secret을 사용하는 클라이언트는 자신의 client secret도 사용합니다. Public client에는 저장할 client secret이 없으므로 임의의 secret을 만들거나 앱에 내장하지 않습니다.
refresh_token 필드에는 access token이 아니라 발급받은 refresh token을 넣습니다. 갱신 응답에는 새 access token과 새 refresh token이 오므로 둘 다 교체해 저장해야 합니다.
Refresh token 교체와 재사용 감지
Foundry는 refresh token을 사용할 때마다 교체합니다. 최초 사용으로부터 1분이 지난 뒤 같은 refresh token을 재사용하면 해당 승인에서 만든 모든 access token이 무효화되고 새 사용자 승인 절차가 필요합니다.
일시적인 네트워크 실패 등을 고려해 최초 사용 후 1분 동안은 같은 refresh token의 재사용을 허용합니다. 이 시간 동안의 재사용 허용을 일반적인 장기 재사용 정책으로 취급하지 않습니다. 30일을 초과해 사용하지 않은 refresh token도 자동 무효화됩니다.
토큰 엔드포인트 참조
POST /multipass/api/oauth2/token
Content-Type: application/x-www-form-urlencoded
본문은 JSON이 아니라 URL 인코딩한 폼입니다.
| 필드 | 형식 | 필수 조건과 의미 |
|---|---|---|
grant_type | String | 필수입니다. authorization_code, refresh_token, client_credentials 중 하나입니다. |
code | String | authorization_code에서 필수인 승인 code입니다. |
refresh_token | String | refresh_token에서 필수인 마지막 refresh token입니다. |
redirect_uri | String | 승인 요청에서 URI를 지정했다면 필수입니다. 사용자 브라우저를 돌려보낼 절대 URI입니다. |
scope | String, 선택 | 요청할 API 권한 범위를 공백으로 구분합니다. |
client_id | String | 필수인 클라이언트 고유 식별자입니다. |
client_secret | String, 조건부 | 앱의 secret입니다. client_credentials에서 필수이며 Public client에는 없습니다. |
code_verifier | String, 조건부 | PKCE 승인 요청을 시작할 때 만든 verifier입니다. PKCE를 사용했다면 교환에 포함합니다. |
성공 응답은 다음 JSON 필드를 포함합니다.
| 필드 | 형식 | 의미 |
|---|---|---|
access_token | String | API 리소스에 접근할 인증 정보입니다. |
token_type | String | 발급한 토큰의 유형입니다. |
expires_in | Number | Access token의 수명을 초 단위로 나타냅니다. |
refresh_token | String, 선택 | 새 access token을 얻을 인증 정보입니다. 갱신할 때마다 교체하므로 최신 값을 저장합니다. |
승인 오류
브라우저 승인 요청이 거부되면 앱의 redirect URI로 돌아갈 수 있습니다. 그 밖의 승인 요청 오류에서는 HTML 오류 페이지를 반환하며 error와 사람이 읽을 수 있는 error_description이 포함됩니다.
| 오류 코드 | 의미 |
|---|---|
invalid_request | 요청이 올바르지 않습니다. |
unauthorized_client | 클라이언트가 authorization code를 요청할 권한이 없습니다. |
access_denied | 서버가 요청을 거부했습니다. |
unsupported_response_type | 요청한 response type을 지원하지 않습니다. |
invalid_scope | Scope가 유효하지 않거나 알 수 없는 값이거나 형식이 잘못되었습니다. |
server_error | 예상하지 못한 서버 오류가 발생했습니다. |
개인 개발용 토큰은 사용자 생성 토큰, 사용자별 데이터 격리와 외부 저장 책임은 외부 API 활용 기준을 참조합니다.