OAuth reference
Auto runs an OAuth 2.1 authorization server for the MCP endpoints. For a walkthrough, see Build an MCP client.
| Issuer | https://app.auto.inc/api |
| Grant types | authorization_code, refresh_token |
| PKCE | Required. S256 only. |
| Client registration | Dynamic (RFC 7591), no approval needed |
| Client auth | none (public), client_secret_basic, client_secret_post |
| Scopes | mcp:images (current), mcp:photos (legacy) |
| Access token | JWT, 1 hour |
| Refresh token | Opaque, 30 days, rotates on use |
| OpenID Connect | Not supported |
Discovery
All discovery documents allow any origin and are cached for an hour.
| Document | URL |
|---|---|
| Authorization server metadata | https://app.auto.inc/.well-known/oauth-authorization-server/api |
| Protected resource, current server | https://app.auto.inc/.well-known/oauth-protected-resource/api/mcp/v1 |
Protected resource, /mcp/v2 | https://app.auto.inc/.well-known/oauth-protected-resource/api/mcp/v2 |
| Protected resource, legacy | https://app.auto.inc/.well-known/oauth-protected-resource/api/mcp/legacy |
For clients that don't insert the issuer path, /.well-known/oauth-authorization-server serves the same metadata. /.well-known/oauth-protected-resource serves the /mcp/v1 document.
Scopes
| Scope | Resource | The user sees |
|---|---|---|
mcp:images | /mcp/v1, /mcp/v2 | List and search your synced camera roll, show those pictures in compatible apps, and organize them into albums in Auto. |
mcp:photos | /mcp/legacy | List and search your photos and show them in compatible apps. |
When you authorize for /mcp/v1, Auto turns a request for mcp:photos into mcp:images. A scope that belongs to another resource is dropped.
POST /api/oauth/register
Registers a client. No authentication.
| Field | Type | Required | Notes |
|---|---|---|---|
client_name | string, 1 to 200 characters | Yes | Shown on the consent page. |
redirect_uris | string[] | Yes | At least one. See the rules below. |
grant_types | string[] | No | Default ["authorization_code", "refresh_token"]. |
response_types | string[] | No | Default ["code"]. |
scope | string | No | Space-separated. Default: all scopes. |
token_endpoint_auth_method | string | No | Default none. |
software_id | string, up to 200 | No | |
software_version | string, up to 200 | No |
Redirect URIs: any https: URI. http: for localhost, 127.0.0.1 and [::1] only. Custom app schemes are allowed. file:, data: and javascript: are refused.
201: the registered client, with client_id and client_id_issued_at. A confidential client also gets client_secret, once. Registering the same public client again returns the same client_id.
400: invalid_client_metadata or invalid_redirect_uri.
GET /api/oauth/authorize
Shows the sign-in and consent page in the user's browser.
| Parameter | Required | Notes |
|---|---|---|
response_type | Yes | code |
client_id | Yes | |
redirect_uri | Yes | Must exactly match a registered URI. |
code_challenge | Yes | 43 to 128 characters. |
code_challenge_method | Yes | S256 |
resource | No | The MCP URL. Default https://app.auto.inc/api/mcp/v1. Always send it. |
scope | No | Default: the client's registered scopes. |
state | No | Returned as is. Always send it. |
On Allow, Auto redirects with code and state. On Deny, it redirects with error=access_denied. Other errors after the redirect URI is checked come back the same way, with error and error_description:
error | Cause |
|---|---|
invalid_request | A parameter is missing or wrong. |
invalid_scope | The scope is unknown, was not registered, or does not apply to the resource. |
invalid_target | The resource is not an Auto MCP URL. |
A bad client_id or redirect_uri shows an error page and does not redirect.
POST /api/oauth/token
Takes application/x-www-form-urlencoded or JSON. Public clients send client_id in the body. Confidential clients use HTTP Basic or client_id plus client_secret in the body.
Authorization code grant
| Field | Required |
|---|---|
grant_type=authorization_code | Yes |
code | Yes. Single use, valid 10 minutes. |
redirect_uri | Yes. Same as in the authorize request. |
code_verifier | Yes. 43 to 128 characters of A-Z a-z 0-9 - . _ ~ |
client_id | Yes, for public clients |
Refresh grant
| Field | Required |
|---|---|
grant_type=refresh_token | Yes |
refresh_token | Yes |
client_id | Yes, for public clients |
200:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "mF4wT0p...y2Qk",
"scope": "mcp:images"
}
Each refresh returns a new refresh token and ends the old one.
Errors: invalid_request, invalid_client (401), invalid_grant, unauthorized_client, unsupported_grant_type.
POST /api/oauth/revoke
Ends a grant (RFC 7009). No client authentication.
| Field | Required | Notes |
|---|---|---|
token | Yes | A refresh token or an access token. |
token_type_hint | No | access_token or refresh_token. |
Revoking either token ends the whole grant. Access tokens stop working on the next request. The answer is 200 even if the token was not valid. A malformed body gets 400 invalid_request.
Calling the MCP server
Send the access token in the Authorization header. Auto does not read tokens from the URL or the body.
POST /api/mcp/v1 HTTP/1.1
Host: app.auto.inc
Authorization: Bearer eyJ...
Content-Type: application/json
Accept: application/json, text/event-stream
| Status | Header |
|---|---|
401 | WWW-Authenticate: Bearer scope="mcp:images", resource_metadata="https://app.auto.inc/.well-known/oauth-protected-resource/api/mcp/v1" |
403 | WWW-Authenticate: Bearer error="insufficient_scope", scope="mcp:images", resource_metadata="..." |
An API key also works as the bearer token. It skips the scope check.