---
title: "Oauth Token"
method: POST
path: "/oauth/token"
tags: ["OAuth"]
---

# Oauth Token

`POST /oauth/token`

## Request

- Base URL: `https://logfire-api.pydantic.dev/api`
- URL: `https://logfire-api.pydantic.dev/api/oauth/token`
- Auth: none declared
- TypeScript: <https://skmtc.dev/pydantic/apis/pydantic-logfire-api/docs/oauth/token/post.ts> (a function with typed inputs; its response is checked against the schema)

OAuth 2.0 token endpoint.

Supports `authorization_code`, `refresh_token`,
`urn:ietf:params:oauth:grant-type:device_code` (RFC 8628), and
`urn:ietf:params:oauth:grant-type:token-exchange` (RFC 8693) grant types.

**SECURITY POLICY:** PKCE (RFC 7636) with S256 is REQUIRED for authorization_code grant.
Clients must provide code_verifier parameter when exchanging authorization codes.

RFC 8707: The `resource` parameter identifies the target resource server for which
the token is being requested.

Per RFC 6749 Section 2.3.1, clients can authenticate via:
- HTTP Basic authentication (Authorization header)
- Request body parameters (client_id and client_secret)

Clients MUST NOT use both authentication methods simultaneously.

Per RFC 6749 Section 4.1.3, client_id is REQUIRED only if the client is not
authenticating with the authorization server (e.g., via Authorization header).

This deployment permits token exchange without separate client authentication
because native/DCR callers are public clients and the signed `subject_token` is
the credential. Exchanges are restricted to same-origin, downscoped resource
tokens. RFC 8693 leaves client authentication to deployment policy.

## Headers

- `authorization` string, nullable
- `user-agent` string, nullable

## Request body `application/x-www-form-urlencoded`

- BodyOauthTokenOauthTokenPost
  - `grant_type` string, required
  - `code` string, nullable
  - `redirect_uri` string, nullable
  - `client_id` string, nullable
  - `client_secret` string, password, nullable
  - `refresh_token` string, password, nullable
  - `scope` string, nullable
  - `code_verifier` string, nullable
  - `resource` string, nullable
  - `device_code` string, password, nullable
  - `client_assertion_type` string, nullable
  - `client_assertion` string, nullable

## Response `200`

Successful Response

- union
  - TokenResponse
    - `access_token` string, required
    - `token_type` 'Bearer', required
    - `expires_in` integer, required
    - `refresh_token` string, required
    - `scope` string, required
  - TokenExchangeResponse: RFC 8693 §2.2 successful token-exchange response. Distinct from the OAuth 2.0 token endpoint's `TokenResponse` (RFC 6749 §5.1): `issued_token_type` is mandatory per §2.2.1, while `refresh_token` is optional and reaches only one of the paths that share this shape. The OIDC workload token is sessionless and the device exchange already hands back a durable API key, so both are the §2.2.1 "one temporary credential for a different temporary credential" case and return none. The Platform exchange returns one when it binds an organization-scoped grant to a project, which `OAuthService.exchange_platform_access_token` documents. `expires_in` is RECOMMENDED, not required, by §2.2.1. All three paths set it: the Platform exchange caps the derived token at the source token's expiry, the OIDC workload path mints a short-lived JWT, and the device-token exchange mints a personal API key that always carries an expiry (see `api_key_token_exchange`).
    - `access_token` string, required
    - `issued_token_type` string, required
    - `token_type` string, required
    - `expires_in` integer
    - `scope` string, required
    - `refresh_token` string

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `422`: Validation Error

---

[API](https://skmtc.dev/pydantic/apis/pydantic-logfire-api.md) - [All operations](https://skmtc.dev/pydantic/apis/pydantic-logfire-api/llms.txt) - [OpenAPI document](https://skmtc.dev/pydantic/apis/pydantic-logfire-api/revisions/7996e45256f4?raw)
