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

# Connected Apps

> OAuth clients registered in your account: how they are bound to one authorization server, what scopes exist, and why revoke is one-way

A **connected app** is an OAuth client registered in your account. It is what an external application uses to obtain a token against one of your MCP gateways.

Manage them in the Portal under **Settings → Connected Apps**.

## The mental model

The important structural fact: **each MCP gateway is its own OAuth authorization server.** There is no single account-wide OAuth server that hands out tokens good everywhere.

A connected app is therefore bound to **one** issuing authorization server, immutably, at registration. Every token grant verifies that the authorization server receiving the request is the one that issued the client before it will mint anything.

```mermaid theme={null}
graph TD
    App["Connected app<br/>(an OAuth client)"]
    App -->|"bound at registration"| AS["Issuing authorization server<br/>= one MCP gateway"]
    AS --> T["Token valid on that gateway only"]
```

That binding is separate from the app's list of permitted audiences, which is a mutable allowlist. One answers "which server minted this credential"; the other answers "which resources may it name". Both are checked.

## How an app gets registered

**Dynamic client registration.** A gateway in the default auth mode exposes `/oauth/register`. A client that supports OpenID Connect dynamic registration can register itself on first use, with no manual step.

**Manually, in the Portal.** Create the client under Settings → Connected Apps: a name, a client type, redirect URIs, allowed scopes, allowed grant types.

## What you configure

| Field               | Values                                                                                             |
| ------------------- | -------------------------------------------------------------------------------------------------- |
| Client type         | **Public** (mobile or SPA, cannot hold a secret — PKCE required) or **Confidential** (server-side) |
| App kind            | First-party or third-party                                                                         |
| Redirect URIs       | HTTPS, or loopback (`http://localhost`, `http://127.0.0.1`) for local development                  |
| Allowed scopes      | Default `mcp`, `profile`                                                                           |
| Allowed grant types | `authorization_code`, `client_credentials`, `refresh_token`, `device_code`                         |
| Token endpoint auth | `none` (public + PKCE), `client_secret_basic`, `client_secret_post`, and others                    |
| PKCE required       | Default on for the authorization-code grant                                                        |

<Note>
  The scopes are `mcp` and `profile`. There are no `read` / `execute` / `manage` / `admin` / `offline_access` scopes anywhere on Noorle — what a token may do comes from the principal behind it, not from a scope string.
</Note>

The allowed-grant-types list is fail-closed: an empty list permits nothing, and a grant is refused unless the client explicitly allows it.

<Warning>
  **Creating a connected app in the Portal does not issue a client secret.** No secret is generated and none is stored, for any client type. If an app needs a confidential-client secret, that has to come from dynamic registration at the gateway.
</Warning>

## Status

| Status        | Meaning                                 |
| ------------- | --------------------------------------- |
| **Active**    | Usable                                  |
| **Suspended** | Temporarily blocked; can be reactivated |
| **Revoked**   | Permanently dead                        |

**Revoke is one-way.** A revoked client cannot be reactivated — it is a kill switch, not a step in a suspend/resume cycle. Use suspend if you might want the app back.

## Who can manage them

Owner, Admin, and Member can create, read, and update connected apps. **Delete requires Delete authority**, which a Member's role does not grant. Restricted principals are excluded unless an explicit grant says otherwise.

## The authorization flow

The gateway performs all protocol validation — client identity, that the redirect URI is registered, PKCE, the resource indicator — *before* any human sees anything.

Only then is the human handed to the Portal for consent, carrying an opaque handoff id and nothing else. The consent page has no ability to influence the protocol parameters, because it never receives them.

After consent, the browser returns to your redirect URI with an authorization code, which your backend exchanges at the gateway's token endpoint.

Two details of that gateway endpoint worth knowing:

* **PKCE is S256 only.** `plain` is not offered.
* The gateway advertises `authorization_response_iss_parameter_supported`, so a client can confirm which authorization server answered.

## Building against it

**Register the redirect URI exactly.** It is matched against the registered list; a trailing slash difference is a mismatch.

**Use PKCE even for confidential clients.** It is required for public clients and costs nothing for the rest.

**Verify `state` on the way back.** Noorle does not do CSRF protection on your behalf for your own redirect.

**Keep a client secret server-side.** If your app cannot, it is a public client — register it as one, with `none` as the token endpoint auth method and PKCE doing the work.

**Handle 401 by re-authorizing**, not by retrying. A revoked or suspended client will never succeed on retry.

## Troubleshooting

| Symptom                     | Cause                                                                            |
| --------------------------- | -------------------------------------------------------------------------------- |
| `invalid_client`            | Wrong client id, or the client is suspended or revoked                           |
| Redirect URI rejected       | It is not in the registered list, or does not match character for character      |
| Grant type refused          | It is not in the client's allowed grant types — that list is fail-closed         |
| Token rejected by a gateway | The client is bound to a *different* gateway as its issuing authorization server |
| PKCE error                  | Only S256 is accepted                                                            |

***

Next: [Roles and Permissions](/docs/learn/auth/roles-and-permissions).
