> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Token Endpoint: 코드 교환·갱신 | Didit Authentication API

> Didit의 POST /token/ 엔드포인트는 PKCE 인가 코드를 교환하거나 refresh token을 교체합니다. refresh token을 재사용하면 세션이 취소됩니다.

> [!WARNING]
>
> 인증 기능은 새로 도입되었습니다. 이 엔드포인트는 `https://<name>.auth.didit.me`와 같이 애플리케이션의 발급자가 제공하며, Didit API 키는 절대 받지 않습니다.

갱신 토큰은 한 번만 사용할 수 있습니다. 이미 사용된 갱신 토큰을 다시 보내면 해당 세션 전체가 취소되므로, 세션마다 갱신은 한 번에 하나씩만 실행하고 세 토큰을 모두 함께 교체하십시오. 자세한 내용은 [갱신 토큰](https://docs.didit.me/ko/authentication/tokens-and-jwks.md#리프레시-토큰)을 참조하십시오.


## OpenAPI

````yaml ko/openapi-authentication.json POST /token/
openapi: 3.0.3
info:
  title: Didit Authentication API
  version: 1.0.0
  description: >-
    애플리케이션의 최종 사용자를 위한 로그인 제품인 Didit Authentication의 서버용 endpoint입니다.
    Authentication은 **새 제품**이며 2026-11-01 릴리스에서 프로덕션에 적용됩니다.


    ## 기본 URL


    모든 애플리케이션은 자체적인 OpenID Connect 발급자(issuer)이며, 아래의 모든 endpoint는 해당 발급자의 루트에서
    제공됩니다.


    - 프로덕션 환경 애플리케이션의 경우 `https://<name>.auth.didit.me`, 샌드박스 애플리케이션의 경우
    `https://<name>.sandbox.auth.didit.me`.

    - 활성화된 커스텀 도메인. 활성화되면 기본 호스트 대신 발급자 역할을 합니다. 새 token의 `iss`에 해당 도메인이 지정되고,
    discovery가 이를 가리키며, 공개 가능 키(publishable key)가 변경됩니다. 기본 호스트에서 발급된 access
    token은 갱신될 때까지 `/session/`, `/step-up/`, `/monitoring-token/` 호출에 실패합니다.


    프로덕션과 샌드박스는 별개의 애플리케이션이므로, 별도의 키를 가진 서로 다른 발급자입니다. 공개 가능 키는 `pk_live_`(프로덕션)
    또는 `pk_test_`(샌드박스) 뒤에 발급자의 패딩 없는 base64url 인코딩(끝의 슬래시 제외)이 붙은 형태입니다. 이를
    디코딩하면 발급자를 확인할 수 있습니다. 이는 공개 식별자이며 자격 증명이 아닙니다.


    동일한 JSON endpoint는
    `https://apx.didit.me/auth/v2/authentication/{application_id}/`에서도 제공됩니다.
    token은 어느 주소에서 발급되었든 항상 위의 발급자를 지정합니다.


    ## 자격 증명


    이러한 endpoint는 Didit API 키나 콘솔 token을 절대 사용하지 않습니다.


    - Discovery, JWKS 및 `POST /token/`은 자격 증명을 요구하지 않습니다. 클라이언트는 공개 클라이언트(public
    client)입니다. 클라이언트 시크릿(client secret)이 없으며, 인가 코드(authorization code)는 PKCE
    검증자(`S256`)에 바인딩됩니다. `client_id`는 애플리케이션 ID입니다.

    - `/session/`, `/step-up/`, `/monitoring-token/`은 사용자의 access token을 사용합니다:
    `Authorization: Bearer <access_token>`. 성공한 각 호출은 세션의 활동으로 간주되므로 세션의 유휴 시간
    초과(idle timeout)가 재시작됩니다.


    ## 요청 본문


    본문을 받는 모든 endpoint는 JSON, 양식 인코딩(form-encoded) 및 multipart 본문을 허용합니다. 이 문서에는
    JSON과 OAuth 클라이언트가 전송하는 `POST /token/`용 양식 인코딩이 나열되어 있습니다. endpoint가 허용하지 않는
    필드는 `400 {"<field>": "unknown_field"}`를 반환합니다.


    ## Token


    - **Access token**: 헤더 `typ: at+jwt`를 가진 ES256 JWT로, 최대 5분 동안 유효하며 해당 세션보다
    오래 유효할 수 없습니다. 클레임: `AccessTokenClaims`.

    - **ID token**: 헤더 `typ: JWT`를 가진 ES256 JWT입니다. 클레임: `IDTokenClaims`. access
    token으로는 절대 허용되지 않습니다.

    - **Refresh token**: 사용할 때마다 순환(rotate)되는 임의의 문자열입니다. 이미 사용된 refresh token을
    다시 보내면 세션이 취소(revoke)됩니다.


    `GET /.well-known/jwks.json`의 키로 token을 검증하세요. 알고리즘은 `ES256`이어야 하고, `typ`은
    token 유형과 일치해야 하며, `iss`는 구성된 발급자와 같아야 하고, `aud`는 애플리케이션 ID여야 합니다. 오프라인
    검증에서는 access token이 만료되어야만 취소된 세션을 감지할 수 있습니다. 즉각적인 확인을 위해서는 `GET
    /session/`을 사용하세요.


    ## 오류


    | 본문 | 조건 |

    | --- | --- |

    | `{"error": "<code>"}` | `POST /token/`에서 발생하는 오류로, 모두 상태 코드 `400`이며 [RFC
    6749 섹션 5.2](https://www.rfc-editor.org/rfc/rfc6749#section-5.2) 형식의 본문을
    갖습니다: `invalid_grant`, `invalid_client` 및 Didit의 `mfa_required`. 서비스 오류:
    `temporarily_unavailable`, `monitoring_unavailable`, `monitoring_disabled`.
    |

    | `{"<field>": ["<message>"]}` 또는 `{"<field>": "<code>"}` | 요청 필드 유효성 검사에
    실패했습니다. `non_field_errors`는 여러 필드에 걸친 규칙 위반을 나타내며, `unknown_field`는
    endpoint가 허용하지 않는 필드를 나타냅니다. |

    | `{"detail": "<message>"}` | `401`(`invalid_token`),
    `403`(`origin_not_allowed`), `404`(알 수 없는 애플리케이션 또는 step-up), `429`(속도 제한).
    또한 파싱할 수 없는 본문의 경우 `400`(`JSON parse error - ...`), endpoint가 지원하지 않는 메서드의
    경우 `405`, 지원되지 않는 `Content-Type`의 경우 `415`. |


    `401` 응답에는 `WWW-Authenticate: Bearer`도 포함되며, `429` 응답에는 초 단위의 `Retry-After`가
    포함됩니다.


    두 가지 거부 응답은 JSON 형식이 아닙니다. 발급자가 아닌 주소는 이러한 endpoint에 도달하기 전에 거부됩니다(예: 사용되지
    않는 `*.auth.didit.me` 이름에 대한 빈 `404`). UUID가 아닌 `step_up_id`는 `404` 페이지를
    반환합니다.


    ## 속도 제한


    요청은 두 계층을 통과해야 합니다.


    클라이언트 IP 주소당, 애플리케이션당, endpoint당 계산되는 **Endpoint 제한**:


    - Discovery 및 JWKS: 분당 1,200건, 시간당 12,000건의 요청.

    - 기타 모든 endpoint: 분당 30건, 시간당 300건의 요청. `GET` 및 `DELETE /session/`은 동일한 카운트를
    공유합니다.


    제한 초과 시: `429 {"detail": "Too many requests from this IP address. Please
    wait before trying again. Expected available in 60 seconds."}` 또는 시간당 제한의 경우
    `3600 seconds`.


    모든 `POST` 및 `DELETE`에 적용되는 **봇 보호**. 해당 버킷은 호스팅된 로그인 단계를 포함한 애플리케이션의 모든
    `POST` 및 `DELETE` 요청이 공유하며 지속적으로 리필됩니다:


    - 클라이언트 IP 주소당 분당 60건의 요청.

    - 브라우저 및 네이티브 SDK와 같이 기기를 식별하는 요청의 경우 기기당 분당 30건의 요청.

    - 모든 IP 주소를 합산한 전체 애플리케이션 기준 분당 3,000건의 요청. token
    갱신(`grant_type=refresh_token`인 `POST /token/`)은 별도의 독립된 버킷을 사용하며 전체 애플리케이션
    기준 분당 6,000건입니다.


    버킷 초과 시: `429 {"detail": "rate_limited Expected available in N
    seconds."}`(N이 1인 경우 `1 second.`). IP별 버킷은 여러 endpoint에 걸쳐 적용되므로, 한 주소에서
    token, step-up, 로그아웃 호출을 섞어 요청하면 특정 endpoint 제한에 도달하기 전에 버킷이 소진될 수 있습니다.
    애플리케이션 버킷은 브라우저를 포함하여 해당 애플리케이션의 모든 클라이언트를 계산합니다.


    두 계층 모두 endpoint가 실행되기 전에 응답하므로, `429`를 받은 요청은 실행되지 않은 상태입니다. 갱신을 포함하여
    `Retry-After` 시간이 지난 후 다시 재시도하세요.


    ## 브라우저 요청


    `Origin` 헤더를 포함하는 요청은 애플리케이션 자체의 origin 또는 허용된 origin 중 하나에서 시작되어야 하며, 그렇지
    않으면 `403 origin_not_allowed`를 받습니다. 서버 간(server-to-server) 요청은 `Origin`을
    전송하지 않습니다.


    ## 여기에 포함되지 않는 내용


    이러한 경로는 호스팅 페이지, 브라우저 및 네이티브 SDK, AI 에이전트를 위한 경로입니다. 서버 규격(contract)에 포함되지
    않으며 서버 SDK는 이를 호출하지 않습니다:


    - 로그인 단계: `POST /authorize/`, `POST /password/`, `/email/*`, `/phone/*`,
    `/password-reset/*`, `/passkeys/*`, `/mfa/*`, `/social/*` 및
    `/protection/complete/`.

    - 호스팅 페이지 및 콜백: `/sign-in`, `/magic`, `/invite`, `/reset`, `/verify/`,
    `/sso/*`, `/wallet/callback/`, `/logo/` 및 `/.well-known/` 하위의 네이티브 앱 연결 파일.

    - 에이전트 액세스: `/agents/*` 및 `/grants/`(AI 에이전트의 access token 실시간 확인을 위한 `GET
    /agents/session/` 포함). 에이전트 액세스는 본 버전의 규격에 포함되지 않습니다.

    - 콘솔 세션을 사용하는 Authentication 콘솔 관리 API.


    Authentication 이벤트는 아직 webhook으로 전송되지 않으므로, 이 문서에서는 webhook을 정의하지 않습니다.
servers:
  - url: https://{issuer}
    description: '귀하 애플리케이션의 발급자: 로그인 오리진입니다.'
    variables:
      issuer:
        default: your-app.auth.didit.me
        description: >-
          발급자 호스트: `<name>.auth.didit.me`(프로덕션),
          `<name>.sandbox.auth.didit.me`(샌드박스) 또는 활성 커스텀 도메인입니다.
security: []
tags:
  - name: Issuer
    description: token을 검증하기 위한 디스커버리 및 서명 키입니다.
  - name: Tokens
    description: 인가 코드 및 갱신 권한 부여입니다.
  - name: Sessions
    description: 액세스 token 뒤의 세션을 확인하고 종료합니다.
  - name: Step-ups
    description: 민감한 작업 전 수행하는 인증입니다.
  - name: Monitoring
    description: 거래 모니터링 이벤트에 대한 기기 자격 증명입니다.
paths:
  /token/:
    post:
      tags:
        - Tokens
      summary: 코드 교환 또는 token 갱신
      description: >-
        두 권한 부여 방식을 모두 지원하는 token endpoint입니다.


        - `authorization_code`는 `redirect_uri`가 수신한 코드를 access, ID 및 refresh
        token으로 교환합니다. 코드는 60초 이내에 PKCE 검증 도구 및 해당 로그인 시의 정확한 `redirect_uri`와 함께
        1회만 작동합니다. 애플리케이션이 1인당 1개 세션만 허용하는 경우, 교환 시 해당 사용자의 다른 세션은 종료됩니다.

        - `refresh_token`은 새로운 access, ID 및 refresh token을 반환하며, 전송한 refresh
        token은 작동을 중단합니다. **이미 사용된 refresh token을 다시 전송하면 세션이 취소되고**
        `invalid_grant`가 반환되므로, 세션당 한 번에 하나의 갱신만 실행하고 세 토큰을 모두 함께 교체하십시오. 갱신이
        `400`으로 실패하거나 응답이 없는 경우, 재시도하지 말고 로그인을 다시 시작하십시오. 성공한 갱신 후의 재시도는 재사용으로
        간주됩니다. `429`는 갱신이 실행되기 전에 응답되므로 `Retry-After` 이후에 안전하게 재시도할 수 있습니다.
        `503`은 갱신이 발생하지 않았으며 refresh token이 여전히 유효함을 의미하므로 백오프를 적용하여 재시도하십시오.


        본문은 JSON 또는 폼 인코딩 형식일 수 있습니다.
      operationId: authentication_token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            examples:
              AuthorizationCode:
                summary: 인가 코드 교환
                value:
                  grant_type: authorization_code
                  code: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
                  code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
                  redirect_uri: https://app.example.com/auth/callback
              RefreshToken:
                summary: token 새로고침
                value:
                  grant_type: refresh_token
                  refresh_token: >-
                    N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: 세션의 새 token입니다.
          headers:
            Cache-Control:
              description: 항상 `no-store`입니다. 이 응답은 절대 캐시하지 마십시오.
              schema:
                type: string
                example: no-store
            Pragma:
              description: 항상 `no-cache`입니다.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              examples:
                Tokens:
                  summary: 새 세션용 token
                  value:
                    access_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
                    id_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
                    refresh_token: >-
                      N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
                    token_type: Bearer
                    expires_in: 299
                    session_id: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
        '400':
          description: >-
            권한 부여에 실패했습니다. `invalid_grant`: code 또는 refresh token을 알 수 없거나, 이미
            사용되었거나, 만료되었습니다. PKCE verifier 또는 `redirect_uri`가 일치하지 않거나,
            `redirect_uri`가 더 이상 등록되어 있지 않습니다. 사용자가 차단되었거나 이메일이 더 이상 인증되지 않은
            상태입니다. 세션이 종료되었거나 MFA 정책을 더 이상 충족하지 않습니다. 또는 code의 경우 생성 출처인 로그인 수단이
            비활성화되었습니다. 로그인 수단을 비활성화해도 이미 시작된 세션의 refresh는 중단되지 않습니다.
            `invalid_client`: `client_id`가 애플리케이션 ID가 아닙니다. `mfa_required`: 로그인이
            MFA 정책을 충족하지 못했습니다. 필드 오류는 누락되었거나 유효하지 않은 필드를 나타냅니다. 파싱할 수 없는 body에는
            `detail` 메시지가 반환됩니다.
          headers:
            Cache-Control:
              description: 항상 `no-store`입니다. 이 응답은 절대 캐시하지 마십시오.
              schema:
                type: string
                example: no-store
            Pragma:
              description: 항상 `no-cache`입니다.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - $ref: '#/components/schemas/FieldErrors'
                  - $ref: '#/components/schemas/Detail'
              examples:
                InvalidGrant:
                  summary: 코드 또는 갱신 token이 거부됨
                  value:
                    error: invalid_grant
                InvalidClient:
                  summary: 잘못된 client_id
                  value:
                    error: invalid_client
                MfaRequired:
                  summary: MFA 정책이 충족되지 않음
                  value:
                    error: mfa_required
                MissingFields:
                  summary: 필수 필드가 누락된 권한 부여
                  value:
                    non_field_errors:
                      - invalid_request
                UnknownGrant:
                  summary: 지원되지 않는 grant_type
                  value:
                    grant_type:
                      - '"password" is not a valid choice.'
                UnknownField:
                  summary: endpoint에서 허용하지 않는 필드
                  value:
                    foo: unknown_field
                MalformedBody:
                  summary: 파싱할 수 없는 JSON 본문
                  value:
                    detail: >-
                      JSON parse error - Expecting property name enclosed in
                      double quotes: line 1 column 2 (char 1)
        '403':
          $ref: '#/components/responses/OriginNotAllowed'
        '404':
          $ref: '#/components/responses/ApplicationNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/TemporarilyUnavailable'
      security:
        - {}
components:
  schemas:
    TokenRequest:
      description: >-
        `grant_type`으로 선택된 두 권한 부여 방식 중 하나입니다. 이 endpoint는 두 방식의 필드를 모두 허용하며 수신된
        모든 필드를 검증하므로, 갱신 요청에 잘못된 형식의 `code_verifier`가 포함되어 있으면 필드 오류가 발생합니다. 해당
        방식에 맞는 필드만 전송하십시오. 다른 필드가 포함되면 `unknown_field`가 반환됩니다.
      oneOf:
        - $ref: '#/components/schemas/AuthorizationCodeGrantRequest'
        - $ref: '#/components/schemas/RefreshTokenGrantRequest'
      discriminator:
        propertyName: grant_type
        mapping:
          authorization_code: '#/components/schemas/AuthorizationCodeGrantRequest'
          refresh_token: '#/components/schemas/RefreshTokenGrantRequest'
    TokenResponse:
      type: object
      description: 한 세션을 위한 token입니다. 갱신 후 세 개를 모두 함께 교체하십시오.
      required:
        - access_token
        - id_token
        - refresh_token
        - token_type
        - expires_in
        - session_id
      properties:
        access_token:
          type: string
          description: >-
            헤더 `typ: at+jwt`의 ES256 JWT입니다. `Authorization: Bearer
            <access_token>` 형식으로 전송하세요. 클레임: `AccessTokenClaims`.
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
          x-didit-jwt:
            header: '#/components/schemas/AccessTokenHeader'
            claims: '#/components/schemas/AccessTokenClaims'
        id_token:
          type: string
          description: >-
            애플리케이션에서 로그인한 사용자를 확인하기 위한 헤더 `typ: JWT`의 ES256 JWT입니다. 액세스 token으로
            전송하지 마세요. 클레임: `IDTokenClaims`.
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
          x-didit-jwt:
            header: '#/components/schemas/IDTokenHeader'
            claims: '#/components/schemas/IDTokenClaims'
        refresh_token:
          type: string
          description: 일회용입니다. 세션이 종료될 때까지 유효하며, 새로고침할 때마다 새로운 token이 반환됩니다.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        token_type:
          type: string
          enum:
            - Bearer
        expires_in:
          type: integer
          minimum: 0
          maximum: 300
          description: 액세스 토큰이 만료될 때까지 남은 시간(초)입니다. 최대 300초이며, 세션이 더 일찍 종료되면 그보다 적습니다.
          example: 299
        session_id:
          type: string
          format: uuid
          description: 해당 token들이 속한 세션으로, `sid` 클레임과 동일한 값입니다.
          example: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
    OAuthError:
      title: OAuth error
      type: object
      description: OAuth 오류(RFC 6749 섹션 5.2) 또는 서비스 오류입니다.
      required:
        - error
      properties:
        error:
          type: string
          example: invalid_grant
    FieldErrors:
      title: Field errors
      type: object
      description: >-
        필드별 유효성 검사 오류입니다. 값은 메시지 목록이거나 단일 코드(`unknown_field`,
        `invalid_redirect_uri`)입니다. `non_field_errors`는 여러 필드에 걸친 규칙을 담습니다.
      additionalProperties:
        oneOf:
          - type: string
          - type: array
            items:
              type: string
    Detail:
      type: object
      required:
        - detail
      properties:
        detail:
          type: string
          example: invalid_token
    AuthorizationCodeGrantRequest:
      title: Authorization code grant
      type: object
      description: 리디렉션에서 받은 인가 코드를 token으로 교환합니다. 이 코드는 60초 동안 1회만 사용할 수 있습니다.
      required:
        - grant_type
        - code
        - code_verifier
        - redirect_uri
      properties:
        grant_type:
          type: string
          enum:
            - authorization_code
        code:
          type: string
          maxLength: 128
          description: '`redirect_uri`가 수신한 `code` 쿼리 파라미터입니다. 교환하기 전에 콜백의 `state`를 확인하십시오.'
          example: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
        code_verifier:
          type: string
          minLength: 43
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]{43,128}$
          description: S256 해시가 로그인의 `code_challenge`였던 PKCE 검증 도구(verifier)입니다.
          example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
        redirect_uri:
          type: string
          maxLength: 2048
          description: >-
            로그인이 시작될 때 사용된 정확한 `redirect_uri`입니다. 여전히 애플리케이션의 리디렉션 URI 중 하나여야
            합니다.
          example: https://app.example.com/auth/callback
        client_id:
          type: string
          maxLength: 64
          description: >-
            선택 사항입니다. 전송 시 애플리케이션 ID와 일치해야 하며, 그렇지 않으면 `invalid_client`로 요청이
            실패합니다.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            선택 사항이며 표준 OpenID Connect 클라이언트에서 허용됩니다. 로그인이 부여한 권한 범위를 절대 확장하지
            않습니다.
          example: openid email
    RefreshTokenGrantRequest:
      title: Refresh token grant
      type: object
      description: >-
        refresh token을 새 access token, ID token, refresh token으로 교환합니다. refresh
        token은 한 번만 작동합니다. 다시 전송하면 세션이 취소되므로, 세션당 한 번에 하나의 새로고침만 실행하십시오.
      required:
        - grant_type
        - refresh_token
      properties:
        grant_type:
          type: string
          enum:
            - refresh_token
        refresh_token:
          type: string
          maxLength: 128
          description: 이전 token 응답의 `refresh_token`입니다.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        client_id:
          type: string
          maxLength: 64
          description: >-
            선택 사항입니다. 전송 시 애플리케이션 ID와 일치해야 하며, 그렇지 않으면 `invalid_client`로 요청이
            실패합니다.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            선택 사항이며 표준 OpenID Connect 클라이언트에서 허용됩니다. 갱신을 수행해도 세션이 보유한 범위가 확장되지
            않습니다.
          example: openid email
  responses:
    OriginNotAllowed:
      description: >-
        요청에 애플리케이션 자체의 오리진도 아니고 허용된 오리진 목록에도 없는 `Origin`이 포함되어 있습니다. 서버 요청은
        `Origin`을 보내지 않습니다.
      headers:
        Cache-Control:
          description: 항상 `no-store`입니다. 이 응답은 절대 캐시하지 마십시오.
          schema:
            type: string
            example: no-store
        Pragma:
          description: 항상 `no-cache`입니다.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            OriginNotAllowed:
              summary: 허용되지 않은 오리진(Origin)
              value:
                detail: origin_not_allowed
    ApplicationNotFound:
      description: >-
        주소가 애플리케이션에 속해 있지만 해당 애플리케이션에 인증이 활성화되어 있지 않습니다. `apx.didit.me` 주소에서는
        애플리케이션 ID를 알 수 없습니다. 어떤 애플리케이션에도 속하지 않는 주소는 endpoint에 도달하기 전에 거부되며 해당
        응답은 JSON이 아닙니다.
      headers:
        Cache-Control:
          description: 항상 `no-store`입니다. 이 응답은 절대 캐시하지 마십시오.
          schema:
            type: string
            example: no-store
        Pragma:
          description: 항상 `no-cache`입니다.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            ApplicationNotFound:
              summary: 알 수 없는 애플리케이션
              value:
                detail: No Configuration matches the given query.
    RateLimited:
      description: >-
        이 endpoint에 대해 해당 IP 주소로부터의 요청이 너무 많거나, `POST` 또는 `DELETE` 요청 시 봇 보호 버킷이
        비어 있습니다. 아무 작업도 실행되지 않았습니다. `Retry-After`초 동안 기다린 후 다시 시도하십시오.
      headers:
        Retry-After:
          description: 재시도하기 전 대기할 시간(초)입니다.
          schema:
            type: integer
            example: 60
        Cache-Control:
          description: 항상 `no-store`입니다. 이 응답은 절대 캐시하지 마십시오.
          schema:
            type: string
            example: no-store
        Pragma:
          description: 항상 `no-cache`입니다.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            RateLimited:
              summary: endpoint 제한
              value:
                detail: >-
                  Too many requests from this IP address. Please wait before
                  trying again. Expected available in 60 seconds.
            BotProtection:
              summary: 봇 방지 제한(POST 및 DELETE)
              value:
                detail: rate_limited Expected available in 2 seconds.
    TemporarilyUnavailable:
      description: 요청이 의존하는 서비스를 일시적으로 사용할 수 없습니다. 백오프를 적용하여 다시 시도하십시오.
      headers:
        Cache-Control:
          description: 항상 `no-store`입니다. 이 응답은 절대 캐시하지 마십시오.
          schema:
            type: string
            example: no-store
        Pragma:
          description: 항상 `no-cache`입니다.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          examples:
            TemporarilyUnavailable:
              summary: 나중에 재시도
              value:
                error: temporarily_unavailable

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.