> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/en/identity-and-access/maiagent-oauth-server.md).

# MaiAgent as an OAuth Authorization Server

Connect MCP clients such as Claude to MaiAgent MCP with OAuth 2.1 (authorization code + PKCE): endpoints, application registration, Redirect URIs, token lifetime and rotation, and how scopes actually

MaiAgent has a built-in OAuth 2.1 authorization server. MCP clients such as Claude can connect to MaiAgent MCP using OAuth without pasting an API Key into a configuration file: after a member signs in through a browser and grants authorization, the client receives a short-lived access token to operate MaiAgent as that member. This page is for two audiences: customer IT teams who want employees to connect to MaiAgent with their own accounts in MCP clients, and developers who want their services to call MaiAgent on behalf of members.

{% hint style="info" %}
**How this page complements the user manual**: For administration steps, see [OAuth Applications](https://docs.maiagent.ai/org/developer/oauth-applications) in the user manual. For MCP client connection settings (API Key and OAuth), see [MaiAgent MCP](/tech/en/mcp/maiagent-mcp.md). This page covers protocol specifications and integration requirements only.
{% endhint %}

This page describes MaiAgent **issuing** tokens. For the reverse direction, where MaiAgent connects to a third-party OAuth service, see [OAuth 2.0 Integration and Automatic Token Refresh](/tech/en/advanced-genai-tech/oauth-integration.md).

## Choosing Between OAuth and API Key <a href="#oauth-or-api-key" id="oauth-or-api-key"></a>

| Item                 | OAuth (this page)                                                                                                     | API Key                                                                                          |
| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Identity represented | The member who grants authorization                                                                                   | The member associated with the key                                                               |
| Setup                | The member signs in and consents through a browser, without handling any secrets                                      | Copy the key into the client settings                                                            |
| Lifetime             | Access tokens last 1 hour; the client refreshes them automatically                                                    | Based on the key's expiration setting; can be set to never expire                                |
| Permissions          | The member's role permissions; scopes do not impose additional restrictions (see [How Scopes Actually Work](#scopes)) | The member's role permissions                                                                    |
| Key RPM limit        | Does not apply                                                                                                        | Applies; see [API Conventions](https://docs.maiagent.ai/api/preparation/conventions#rate-limits) |

Members who sign in only through SSO and have no MaiAgent password can also authorize OAuth through their organization accounts. See [Sign-in and Consent](#login-and-consent).

## Endpoints <a href="#endpoints" id="endpoints"></a>

The SaaS authorization server is `https://api.maiagent.ai`. For on-premises and private cloud deployments, replace it with your company's platform domain. Metadata URLs are generated using the domain of the request.

| Purpose                                  | Method and Path                               |
| ---------------------------------------- | --------------------------------------------- |
| Authorization server metadata (RFC 8414) | `GET /.well-known/oauth-authorization-server` |
| Authorization                            | `GET /o/authorize/`                           |
| Token exchange                           | `POST /o/token/`                              |
| Token revocation (RFC 7009)              | `POST /o/revoke_token/`                       |

The capabilities declared in the metadata match the actual behavior:

| Field                                   | Value                                               |
| --------------------------------------- | --------------------------------------------------- |
| `response_types_supported`              | `code`                                              |
| `grant_types_supported`                 | `authorization_code`, `refresh_token`               |
| `code_challenge_methods_supported`      | `S256`                                              |
| `token_endpoint_auth_methods_supported` | `none`, `client_secret_basic`, `client_secret_post` |
| `scopes_supported`                      | `read`, `write`                                     |

{% hint style="warning" %}
MaiAgent **does not support Dynamic Client Registration**, and the metadata has no `registration_endpoint`. MCP clients must allow you to enter a preregistered Client ID. Register an application in your organization first, as described in the next section.
{% endhint %}

## Registering an OAuth Application in Your Organization <a href="#register-application" id="register-application"></a>

In the administration interface, go to <mark style="color:blue;">Organization Settings</mark> → <mark style="color:blue;">Organization Overview</mark> → <mark style="color:blue;">Management Actions</mark> → <mark style="color:blue;">OAuth Applications</mark> to create an application and obtain its Client ID. The <mark style="color:blue;">Connection Information</mark> card lists the Discovery, Authorize, and Token endpoints. For creation steps, who can manage applications, and how to store and regenerate the Client Secret, see [OAuth Applications](https://docs.maiagent.ai/org/developer/oauth-applications) in the user manual. Keep these integration requirements in mind:

* **The application type cannot be changed after creation**: Public applications are for MCP clients (such as Claude), use PKCE for authorization, and receive no client secret. Confidential applications are for server-side integrations and receive a client secret.
* Regenerating the Client Secret immediately invalidates the old secret but does not affect tokens already issued. Deleting the application immediately invalidates all tokens for every member connected through it.
* Each organization can have up to 20 OAuth applications.
* The same functionality is available through the API: `/api/v1/oauth/applications/` (GET, POST, PATCH, DELETE, plus `POST …/{id}/regenerate-secret/`). Permission rules are the same as in the administration interface.

## Redirect URI Rules <a href="#redirect-uris" id="redirect-uris"></a>

* **Exact matching**: The scheme, host, port, and path must all match. Wildcards are not supported. If the registered URI includes query parameters, the authorization request must include them too.
* Only `https://` is accepted. `http://` is allowed only for local callbacks: `localhost`, `127.0.0.1`, and `::1`.
* Local callback ports: When you register `http://127.0.0.1/…` or `http://[::1]/…`, the authorization request can use any port (RFC 8252). When you register `http://localhost:<port>/…`, the port must match exactly. If your local MCP client chooses a random port each time, register the `127.0.0.1` form.
* For web-based MCP clients, follow the client's documentation for the callback URL. The example in the administration form is `https://claude.ai/api/mcp/auth_callback`.

## Authorization Flow <a href="#authorization-flow" id="authorization-flow"></a>

When an MCP client connects to MaiAgent MCP (`https://mcp.maiagent.ai/mcp` on SaaS), the flow is as follows:

1. The client sends no credentials. The MCP server returns `401`, with the `WWW-Authenticate` header pointing to `https://mcp.maiagent.ai/.well-known/oauth-protected-resource/mcp`.
2. The client reads this resource metadata (RFC 9728). Its `authorization_servers` points to `https://api.maiagent.ai`, and the client then reads the authorization server metadata.
3. The client opens a browser at `/o/authorize/`, where the member signs in and consents.
4. MaiAgent redirects the authorization code to the Redirect URI. The client sends `code_verifier` to `/o/token/` to exchange the code for tokens.
5. Every subsequent call includes `Authorization: Bearer <access_token>`. When the access token expires or is revoked, the MCP server returns `401`, and the client uses the refresh token to obtain a new one.

If you implement your own client, use the following formats for authorization and token exchange requests:

```http
GET /o/authorize/?response_type=code&client_id=<CLIENT_ID>&redirect_uri=<REDIRECT_URI>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256&state=<STATE>
```

```bash
curl -X POST https://api.maiagent.ai/o/token/ \
  -d grant_type=authorization_code \
  -d code=<CODE> \
  -d redirect_uri=<REDIRECT_URI> \
  -d client_id=<CLIENT_ID> \
  -d code_verifier=<CODE_VERIFIER>
```

* **PKCE is always required**, for both Public and Confidential applications. Use `S256` for `code_challenge_method`.
* Confidential applications must also provide the secret using HTTP Basic (`client_secret_basic`) or the `client_secret` form field (`client_secret_post`).
* Authorization codes are valid for 300 seconds.
* The token exchange response includes `access_token`, `token_type` (`Bearer`), `expires_in` (`3600`), `refresh_token`, and `scope`.

### Sign-in and Consent <a href="#login-and-consent" id="login-and-consent"></a>

* The authorization page retains Email and password sign-in. When continuing authorization, it also offers <mark style="color:blue;">Sign in with an organization account (SSO)</mark>. This entry point completes sign-in through the administration interface and then returns to the authorization page, so members who use only SSO and have no MaiAgent password can also authorize access.
* The consent page shows the application name, its organization, and the Email of the member currently signed in.
* If the member already has a valid token for this application, reconnecting skips the consent page.

### SSO Sign-in Handoff and Security Boundaries <a href="#sso-login-handoff" id="sso-login-handoff"></a>

For example, an employee normally signs in to MaiAgent using SAML and has not set a separate MaiAgent password. When an MCP client such as Claude opens the authorization page, the employee chooses organization account sign-in, completes their usual sign-in to the administration interface, returns to the authorization page, and then consents to the client connecting as them.

The SSO handoff is a browser sign-in flow. It does not replace the client's authorization code and PKCE exchange: the client still uses the authorization and token endpoints above and does not need to handle the administration sign-in handoff code itself. Integrators should be aware of the following:

* The signed sign-in request received by the administration interface is valid for 10 minutes. The one-time handoff code used after sign-in is valid for 300 seconds and can be redeemed only once. This handoff code serves a different purpose from the OAuth authorization code received by the MCP client.
* The handoff code is bound to the browser that initiated sign-in and only continues this site's `/o/authorize/` flow. It cannot be used in another browser, and an external site cannot be specified as the redirect destination after sign-in.
* The handoff cannot complete if the account is disabled or its SSO authentication source has sign-in disabled. When a member explicitly signs out, sign-in attempts that were still incomplete before sign-out also become invalid.
* API Keys and member embedding tokens cannot be exchanged for sign-in handoff codes. They cannot replace a member completing sign-in and authorization in the browser.

{% hint style="warning" %}
If the sign-in handoff is incomplete, times out, or becomes invalid, start again from the authorization sign-in page. Do not resubmit an old handoff code.
{% endhint %}

### Token Identity and Organization <a href="#token-identity" id="token-identity"></a>

* The token represents the member who grants authorization. Its organization is fixed to **the organization that owns the application**. Calls do not need or rely on the `X-Organization-Id` header.
* At the time of the call, this person must be an active member of that organization. If the member is disabled or removed from the organization, or the organization is deleted, the token immediately becomes unusable and the API returns `401`.
* Access tokens can also call the platform REST API directly (`Authorization: Bearer <access_token>`), with the same permission checks as API Keys. Exceptions: AI Gateway's OpenAI-compatible endpoints accept only API Keys, and high-risk operations such as deleting your own account accept only interactive sign-in.

## Token Lifetime and Rotation <a href="#token-lifetime" id="token-lifetime"></a>

| Item                        | Rule                                                                                                                                                                                                                                                    |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization code          | 300 seconds                                                                                                                                                                                                                                             |
| Access token                | 3600 seconds (1 hour)                                                                                                                                                                                                                                   |
| Refresh token               | Rotated on every use: the response contains a new refresh token, and the old one is invalidated                                                                                                                                                         |
| Resubmission after rotation | When the same request is resubmitted due to network issues or concurrency, the old refresh token that was just rotated remains usable for 120 seconds                                                                                                   |
| Explicit revocation         | Takes effect immediately, with no 120-second grace period                                                                                                                                                                                               |
| Refresh token lifetime      | Each refresh token is valid for 30 days from issuance. Every successful refresh rotates it to a new token, and the 30-day lifetime starts again from the new token's issuance time. If no refresh occurs before expiration, reauthorization is required |

When your integration receives `invalid_grant`, stop using that refresh token and prompt the member to authorize again. Do not retry in the background. For details on revocation and rotation, see [When a Refresh Token Is Revoked or Becomes Invalid](#refresh-token-revocation).

There are three ways to revoke tokens:

| Who Revokes                | Method                                                                                                                                                       | Scope of Impact                                   |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- |
| Integrator                 | `POST /o/revoke_token/`, with `token` (optionally `token_type_hint`) and client authentication                                                               | A single token                                    |
| The member                 | `GET /api/v1/oauth/authorized-applications/` lists their authorized applications; `DELETE /api/v1/oauth/authorized-applications/{id}/` revokes authorization | All tokens this member granted to the application |
| Organization administrator | Delete the OAuth application                                                                                                                                 | All tokens all members granted to the application |

### When a Refresh Token Is Revoked or Becomes Invalid <a href="#refresh-token-revocation" id="refresh-token-revocation"></a>

Once a refresh token is explicitly revoked, it immediately stops being usable to obtain access tokens. If your integration continues sending it to `POST /o/token/`, it receives HTTP 400 with the error code `invalid_grant`. Handle this as follows:

1. When you receive `invalid_grant`, stop using that refresh token and prompt the member to authorize again. Do not retry in the background.
2. When a member revokes an entire authorized application, all tokens they granted to that application are revoked, and all old refresh tokens become unusable.
3. After normal rotation, when the same request is resubmitted due to network issues or concurrency, the old refresh token that was just rotated has a 120-second retry grace period. This grace period does not apply to explicit revocation.

{% hint style="warning" %}
Treat `invalid_grant` as a signal to reauthorize: do not assume that a token remains usable for 120 seconds after explicit revocation. The 30-day lifetime is an upper limit, not a guarantee that the token will remain usable throughout that period. Revocation, application deletion, or member deactivation can invalidate authorization earlier.
{% endhint %}

## How Scopes Actually Work <a href="#scopes" id="scopes"></a>

The authorization server accepts two scopes: `read` and `write`. If the authorization request omits `scope`, both are granted by default. **Scopes currently do not restrict any API**: what a member can do is determined entirely by their role permissions, just as when they sign in to the administration interface or use an API Key. To restrict what an MCP client can do, create a dedicated role for its user. See [Role and Permission Management](https://docs.maiagent.ai/org/roles/role-permission) in the user manual.

## Frequently Asked Questions <a href="#faq" id="faq"></a>

<details>

<summary>The MCP client cannot find a registration endpoint or cannot register?</summary>

MaiAgent does not support Dynamic Client Registration. Create an application in <mark style="color:blue;">OAuth Applications</mark> first, then enter its Client ID (and Client Secret for Confidential applications) in the MCP client's OAuth settings.

</details>

<details>

<summary>A redirect_uri mismatch occurs during authorization?</summary>

Redirect URIs use exact matching. Make sure the scheme, host, port, and path exactly match the registered values. If a local client changes ports each time, register the `http://127.0.0.1/…` form instead.

</details>

<details>

<summary>Reauthorization is requested after being connected for a while?</summary>

If a refresh token has not been refreshed for more than 30 days since issuance, or has been revoked, the token endpoint returns `invalid_grant`, and the client must ask the member to authorize again. Once a member is disabled, API calls with access tokens are rejected (HTTP 401). Regenerating the client secret does not invalidate tokens already issued.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/tech/en/identity-and-access/maiagent-oauth-server.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
