OAuth API
The OAuth API is the OAuth 2.0 surface for token issuance, refresh, and introspection. The authorization endpoint lives on the auth service; the token and refresh endpoints live here on public-api.
For the conceptual overview of how OAuth fits into Skynet — grant types, scopes, client registration, personal token semantics — see Auth. This page covers the endpoints themselves.
Endpoints at a glance
| Endpoint | Purpose |
|---|---|
POST /oauth/token |
Exchange an authorization code for access + refresh tokens (auth code grant only) |
POST /oauth/token/refresh |
Exchange a refresh token for a new access (and refresh) token |
POST /oauth/token/introspect |
Inspect a token's validity and metadata (admin / first-party clients only) |
POST /oauth/token/revoke |
Revoke an active token |
The authorization endpoint (/oauth/authorize) is served by
apps/auth/, not by this router.
Supported flows
- Authorization code with PKCE. The only grant type supported
for new code exchange. The authorization endpoint accepts a
code_challenge; the token endpoint verifies it. - Refresh token rotation. Calling
/oauth/token/refreshreturns a new refresh token; the old one is invalidated. Always replace the stored refresh token with the new one.
There is no client_credentials, password, or implicit grant.
See Auth — Grant types.
Client identifiers
Every token-endpoint call carries client_id and (for confidential
clients) client_secret. Confidential clients send the secret in
the request body; public clients omit it and rely on PKCE for
authorization-code verification.
Common pitfalls
- Missing PKCE on the authorize step. The token endpoint will reject the code exchange if the authorize step didn't include a code challenge.
- Reusing an authorization code. Codes are single-use; trying to reuse one returns an error.
- Refresh token rotation. When refresh returns a new refresh token, replace the stored copy immediately or you'll be locked out at the next refresh.
Reference
OAuth API 2.0.0
Endpoints for managing OAuth authentication and authorization.
OAuth
GET /oauth/clients
Get Oauth Clients
Description
Return all registered OAuth clients.
Responses
[
{
"allowedScopes": null,
"clientId": "string",
"createdAt": null,
"description": null,
"logoUrl": null,
"name": "string",
"redirectUris": [
"string"
]
}
]
Schema of the response body
{
"items": {
"$ref": "#/components/schemas/OAuthClient"
},
"title": "Response Get Oauth Clients Oauth Clients Get",
"type": "array"
}
GET /oauth/clients/{client_id}
Get Oauth Client
Description
Return a specific registered OAuth client.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
client_id |
path | string | No |
Responses
{
"allowedScopes": null,
"clientId": "string",
"createdAt": null,
"description": null,
"logoUrl": null,
"name": "string",
"redirectUris": [
"string"
]
}
Schema of the response body
{
"properties": {
"allowedScopes": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"description": "Allowed scopes"
},
"clientId": {
"description": "Unique client identifier",
"type": "string"
},
"createdAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Creation timestamp"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Description of the client"
},
"logoUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Logo URL"
},
"name": {
"description": "Name of the client",
"type": "string"
},
"redirectUris": {
"description": "Allowed redirect URIs",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"clientId",
"redirectUris",
"name"
],
"title": "OAuthClient",
"type": "object"
}
{
"detail": [
{
"ctx": {},
"input": null,
"loc": [
null
],
"msg": "string",
"type": "string"
}
]
}
Schema of the response body
{
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"title": "Detail",
"type": "array"
}
},
"title": "HTTPValidationError",
"type": "object"
}
POST /oauth/sign-out
Sign Out Post
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
token |
query | No |
Responses
{
"detail": [
{
"ctx": {},
"input": null,
"loc": [
null
],
"msg": "string",
"type": "string"
}
]
}
Schema of the response body
{
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"title": "Detail",
"type": "array"
}
},
"title": "HTTPValidationError",
"type": "object"
}
POST /oauth/token
Token
Description
Exchange an authorization code for an access token using PKCE.
Request body
{
"clientId": "string",
"code": "string",
"codeVerifier": null,
"grantType": "string",
"redirectUri": "string"
}
Schema of the request body
{
"description": "Request model for token exchange.",
"properties": {
"clientId": {
"description": "The client ID of the application making the request",
"type": "string"
},
"code": {
"description": "The authorization code received from the authorization server",
"type": "string"
},
"codeVerifier": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The code verifier used in PKCE (Proof Key for Code Exchange)"
},
"grantType": {
"description": "The type of grant being requested",
"type": "string"
},
"redirectUri": {
"description": "The URI to redirect to after the token exchange",
"type": "string"
}
},
"required": [
"grantType",
"code",
"redirectUri",
"clientId"
],
"title": "TokenRequest",
"type": "object"
}
Responses
{
"accessToken": "string",
"expiresIn": 10.12,
"issuedAt": 10.12,
"refreshToken": null,
"tokenType": "string"
}
Schema of the response body
{
"description": "Response model for token exchange.",
"properties": {
"accessToken": {
"description": "Access token issued by the authorization server",
"type": "string"
},
"expiresIn": {
"description": "Duration in seconds until the token expires",
"type": "number"
},
"issuedAt": {
"description": "Timestamp when the token was issued (UNIX timestamp)",
"type": "number"
},
"refreshToken": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Refresh token issued by the authorization server"
},
"tokenType": {
"default": "Bearer",
"description": "Type of the token issued, typically 'Bearer'",
"type": "string"
}
},
"required": [
"accessToken",
"expiresIn",
"issuedAt"
],
"title": "TokenResponse",
"type": "object"
}
{
"detail": [
{
"ctx": {},
"input": null,
"loc": [
null
],
"msg": "string",
"type": "string"
}
]
}
Schema of the response body
{
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"title": "Detail",
"type": "array"
}
},
"title": "HTTPValidationError",
"type": "object"
}
POST /oauth/token/refresh
Refresh Token Post
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
token |
query | No |
Request body
{
"refresh_token": null
}
Schema of the request body
{
"properties": {
"refresh_token": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Refresh Token"
}
},
"title": "Body_refresh_token_post_oauth_token_refresh_post",
"type": "object"
}
Responses
{
"accessToken": "string",
"expiresIn": 10.12,
"issuedAt": 10.12,
"refreshToken": null,
"tokenType": "string"
}
Schema of the response body
{
"description": "Response model for token exchange.",
"properties": {
"accessToken": {
"description": "Access token issued by the authorization server",
"type": "string"
},
"expiresIn": {
"description": "Duration in seconds until the token expires",
"type": "number"
},
"issuedAt": {
"description": "Timestamp when the token was issued (UNIX timestamp)",
"type": "number"
},
"refreshToken": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Refresh token issued by the authorization server"
},
"tokenType": {
"default": "Bearer",
"description": "Type of the token issued, typically 'Bearer'",
"type": "string"
}
},
"required": [
"accessToken",
"expiresIn",
"issuedAt"
],
"title": "TokenResponse",
"type": "object"
}
{
"detail": [
{
"ctx": {},
"input": null,
"loc": [
null
],
"msg": "string",
"type": "string"
}
]
}
Schema of the response body
{
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"title": "Detail",
"type": "array"
}
},
"title": "HTTPValidationError",
"type": "object"
}
Schemas
Body_refresh_token_post_oauth_token_refresh_post
| Name | Type | Description |
|---|---|---|
refresh_token |
HTTPValidationError
| Name | Type | Description |
|---|---|---|
detail |
Array<ValidationError> |
OAuthClient
| Name | Type | Description |
|---|---|---|
allowedScopes |
Allowed scopes | |
clientId |
string | Unique client identifier |
createdAt |
Creation timestamp | |
description |
Description of the client | |
logoUrl |
Logo URL | |
name |
string | Name of the client |
redirectUris |
Array<string> | Allowed redirect URIs |
TokenRequest
| Name | Type | Description |
|---|---|---|
clientId |
string | The client ID of the application making the request |
code |
string | The authorization code received from the authorization server |
codeVerifier |
The code verifier used in PKCE (Proof Key for Code Exchange) | |
grantType |
string | The type of grant being requested |
redirectUri |
string | The URI to redirect to after the token exchange |
TokenResponse
| Name | Type | Description |
|---|---|---|
accessToken |
string | Access token issued by the authorization server |
expiresIn |
number | Duration in seconds until the token expires |
issuedAt |
number | Timestamp when the token was issued (UNIX timestamp) |
refreshToken |
Refresh token issued by the authorization server | |
tokenType |
string | Type of the token issued, typically 'Bearer' |
ValidationError
| Name | Type | Description |
|---|---|---|
ctx |
||
input |
||
loc |
Array<> | |
msg |
string | |
type |
string |