> ## 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.

# Authentication Overview

> Three separate token issuers, four principal types, and which credential belongs on which surface

Noorle has **three distinct issuers of credentials**, and conflating them is the most common source of confusion. Start here.

| Issuer                | Host                      | Issues                                                        | Signed with                             |
| --------------------- | ------------------------- | ------------------------------------------------------------- | --------------------------------------- |
| **Identity boundary** | `auth.noorle.com`         | An OIDC `id_token` proving who a principal is                 | RS256, key published at a JWKS endpoint |
| **Platform**          | `api.noorle.com`          | Access tokens for the management API, via the CLI device flow | HS256, symmetric — no JWKS              |
| **Each MCP gateway**  | `mcp-{handle}.noorle.com` | Tokens valid on that gateway only                             | HS256, symmetric — no JWKS              |

They are not interchangeable. A token minted by one gateway does not work on another; a boundary `id_token` is not a management-API access token.

**API keys are separate from all three.** They are platform-local, never boundary-issued. If the identity boundary is down, sign-in fails but API-key verification keeps working.

## Which credential do I need?

| Situation                                    | Use                                                     |
| -------------------------------------------- | ------------------------------------------------------- |
| Working in the Portal                        | Nothing — sign in and you have a session                |
| A backend service calling the management API | An [API key](/docs/learn/auth/api-keys) on a service account |
| A CLI or headless tool                       | The [device flow](/docs/learn/auth/oauth-device-flow)        |
| An MCP client connecting to your gateway     | The gateway's own OAuth flow, or an API key             |
| A third-party app acting for your account    | A [connected app](/docs/learn/auth/connected-apps)           |

<Note>
  Start with the simplest thing that meets your requirement. Moving from an API key to OAuth later is a configuration change, not a rewrite.
</Note>

## Human sign-in

Sign-in is **passwordless**. There is no password to set, store, or rotate.

```mermaid theme={null}
graph TD
    A["Enter your email in the Portal"]
    B["Boundary sends a magic link<br/>and shows a manual code"]
    C1["Click the link"]
    C2["…or type the XXXX-XXXX code<br/>on the device you started from"]
    D["Boundary redirects back with a code"]
    E["Portal exchanges it back-channel<br/>for an RS256 id_token"]
    F["Portal session cookie"]

    A --> B
    B --> C1
    B --> C2
    C1 --> D
    C2 --> D
    D --> E
    E --> F
```

The magic link carries an opaque single-use token and no destination. The manual code exists for the cross-device case — you start on a laptop, the mail arrives on your phone. Its alphabet deliberately excludes visually ambiguous characters.

Guardrails on that flow:

| Limit                                | Value          |
| ------------------------------------ | -------------- |
| Pending sign-in transaction lifetime | **15 minutes** |
| One-time exchange code lifetime      | **60 seconds** |
| Manual-code attempts per transaction | **5**          |
| Magic-link sends per transaction     | **3**          |
| `id_token` lifetime                  | **5 minutes**  |

PKCE is required, **S256 only**. `plain` is refused, and an omitted method defaults to S256 rather than to `plain` — stricter than the RFC requires.

The boundary advertises exactly one response type (`code`), one grant type (`authorization_code`), one signing algorithm (`RS256`), and one client authentication method (`client_secret_basic`). There is no device flow and no refresh token at the boundary.

<Warning>
  The `access_token` field in the boundary's `/token` response is an inert placeholder. The RS256-signed `id_token` is the sole carrier of identity. Do not build against that `access_token`.
</Warning>

### Sessions

Once signed in, the Portal holds a signed, host-only session cookie.

Its lifetime is a **sliding 20-hour inactivity window**, not a fixed 20-hour lifetime — activity refreshes it. The anchor session at the identity boundary is separate and much longer-lived, which is what lets you return to the Portal without re-entering your email.

## Principal types

Four kinds of principal exist: **User**, **Service**, **Agent**, and **System**.

* **User** — a person. Authenticates by signing in; may also hold an API key.
* **Service** — a service account. Authenticates by API key. This is what a backend integration should be.
* **Agent** — an agent acting on its own behalf. Created alongside the agent; the principal id *is* the agent id.
* **System** — the platform itself. Never authenticates.

The boundary mints only `service` and `agent` principals on request; `human` and `system` are refused at that surface.

An agent making an outbound call carries a delegation chain: the agent is the immediate actor, and whoever triggered the run is preserved as a nested claim. Chain depth is capped at 5.

## Roles

Four account roles, in increasing authority: **Restricted**, **Member**, **Admin**, **Owner**.

They map to authority like this:

| Role         | Authority from the role alone                                      |
| ------------ | ------------------------------------------------------------------ |
| Owner, Admin | Everything except resolving an autonomy gate on a service's behalf |
| Member       | Everything except **delete** and gate resolution                   |
| Restricted   | **Nothing.** Only what an explicit grant gives it                  |

Explicit grants sit on top of the role and can give a Restricted principal exactly the access it needs. See [Roles and Permissions](/docs/learn/auth/roles-and-permissions).

<Note>
  Those four are the whole set. There is no "Developer" role.
</Note>

## How a request is authorized

Two different questions get asked, in two different places.

**"May this principal manage this resource?"** — the management plane. Handlers do not decide this themselves. They build an identity context at the edge and hand it to a per-resource service, which re-reads role and grants **per operation** rather than freezing them for the request.

An authority denial is **403**. A resource that does not exist under the account floor is **404**. Validation runs only *after* authority, so a denied caller cannot use error shapes to probe what exists.

**"May this agent make this tool call?"** — the admission door. Different question, different code path. See [Agents](/docs/learn/concepts/agents#autonomy).

## Encryption

| Data                  | At rest                                          |
| --------------------- | ------------------------------------------------ |
| API key secrets       | Hashed, never recoverable                        |
| Connector credentials | AES-256-GCM                                      |
| Plugin credentials    | AES-256-GCM, decrypted only at the host boundary |
| Session cookie        | Signed, `HttpOnly`, `SameSite=Lax`, `Secure`     |

## The audit record

Every autonomy decision is appended to the journal, carrying who acted, who invoked, who resolved a gate if one was raised, the tool, the risk tier, the execution surface, and the decision source. Management-plane denials are journaled through the same event.

The Portal exposes this per agent under **Audit**, with CSV export.

## Practical guidance

<CardGroup cols={2}>
  <Card title="Service accounts, not personal keys" icon="robot">
    A backend service should authenticate as its own service principal. A key tied to a person breaks when that person's access changes.
  </Card>

  <Card title="Grants, not role escalation" icon="target">
    Needing one extra capability is a reason to add a grant, not to promote a principal to Admin.
  </Card>

  <Card title="Never commit a key" icon="shield">
    API keys are bearer credentials. Environment variables or a secret manager, never version control.
  </Card>

  <Card title="Revoke, do not wait for expiry" icon="power-off">
    Revocation takes effect immediately. Expiry is optional and most keys do not have one set.
  </Card>
</CardGroup>

***

Next: [API Keys](/docs/learn/auth/api-keys).
