# Tokens and sign-in

> Every token in your traffic, decoded, with expiry and every host it went to, and OAuth or OIDC sign-ins as a timeline. Flags weak algorithms and missing PKCE.

Source: https://docs.subcloak.com/cloak-proxy/security/tokens-and-sign-in/

Added in Cloak Proxy 0.5.1.

**Tools › Tokens and Sign-in…** (<kbd>⌥</kbd> <kbd>⌘</kbd> <kbd>J</kbd>) finds the credentials in the captured traffic and checks them:

- **Tokens**: bearer tokens and JWTs in `Authorization` and token headers, session cookies, tokens in query strings and request bodies, and the ones token endpoints and sign-in redirects hand out. For each: its claims decoded, a live expiry countdown, the response that issued it, and a **sent to** map of every host and place it went.
- **Sign-in flows**: OAuth 2.0 and OpenID Connect hops (the authorization request, the sign-in pages, the redirect back with a code, the token request, the user-info call) grouped into one timeline per sign-in, plus refreshes, client-credentials and password grants.
- **Findings** for both, worst first.

It only reads what you've captured: nothing is sent, so it's safe to run against anything. The [inspector's Auth tab](https://docs.subcloak.com/cloak-proxy/inspect/inspector/) decodes one request's token; this window looks at all of them together, across requests.

*[Screenshot: The Tokens and sign-in window on the Tokens tab. The list on the left shows two access tokens that never expire with red high-severity marks, ShopApp's access token for ada@example.com (expires in 5 minutes, sent to auth.example.com, api.example.com and one more), a refresh token and an ID token. On the right the selected access token: JWT pill, a masked value with Show, Issued by POST auth.example.com/oauth2/token (body access_token), tiles for Expiry (Expires in 5 minutes), Issued, Expires and Lifetime 15 minutes, findings for Sent to a host its audience doesn't name, Sent to 2 sites, Personal data in the payload and Also sent outside the scope, each with Show Requests, and the decoded header and claims with what each standard claim means.. One access token: where it came from, how long it has left, everywhere it went, and what's wrong.]*

## Tokens

Select a token for its details:

| Part | What it shows |
|---|---|
| Heading | What it is (access, ID, refresh or session token), the user it's for, **JWT** or **Opaque**, and the value masked (**Show** reveals it, **Copy** copies it whole) |
| **Issued by** | The response that handed it out (a token endpoint's body, a `Set-Cookie`, a redirect's URL), linked to the request. When it isn't there, the token came from an earlier session, another device or a pinned connection |
| Expiry | Time left, ticking (orange in the last five minutes, red once expired), when it was issued and expires, and its lifetime. Opaque tokens use the `expires_in` from the response that issued them |
| **Findings** | What's wrong with it, each with **Show Requests** |
| **Header and claims** | Every header field and claim, with the dates of `exp`, `iat` and `nbf` and what the standard claims mean. Decoded, not verified: Cloak Proxy doesn't check signatures |
| **Sent to** | Each host and place it was sent (`header Authorization`, `cookie session`, `query access_token`, `body refresh_token`), how many requests, whether over plain HTTP, and whether the host is [in scope](https://docs.subcloak.com/cloak-proxy/security/scope/) |

**Trace Value** runs [Trace Value](https://docs.subcloak.com/cloak-proxy/inspect/trace-value/) on the token and adds **Everywhere it went**: every endpoint that carried it in any header, body or URL, including places the sent-to map doesn't look, such as a `Referer` or a JSON body sent to an analytics service.

The right-click menu in the traffic list has **Inspect Tokens…** for a request with an `Authorization` header or cookies, and the Auth tab's JWT bar shows the time left and quick warnings, with **Tokens and Sign-in…**.

### Token findings

| Finding | Severity | Why it matters |
|---|---|---|
| Unsigned token (`alg none`) | High | A server that accepts it accepts any claims anyone writes |
| No signature | High | The header names an algorithm but the signature is empty |
| Accepted after it expired | High | A request sent it after `exp` and got a 2xx: the server may not check expiry |
| Sent over plain HTTP | High | Anyone on the network can copy it (loopback is fine) |
| The header names its own key (`jku`, `jwk`, `x5u`, `x5c`) | Medium | A server that trusts a key from the token itself accepts forged tokens |
| Never expires | Medium | No `exp`: a stolen copy works until the key changes |
| Valid for more than 30 days | Medium | (More than a day is Low.) Long-lived access tokens are worth more to whoever copies them |
| Sent to a host its audience doesn't name | Medium | `aud` names other hosts: a server that doesn't check it accepts tokens meant for another service |
| Issuer over plain HTTP | Medium | Keys and metadata fetched from it can be tampered with |
| Secrets in the payload | Medium | A claim like `password` or `secret`: payloads are encoded, not encrypted |
| Sent in a URL | Medium | URLs end up in logs, history and `Referer` headers |
| Sent to several sites | Medium | Each of them can use it as this user |
| Signed with a shared secret (`HS256`) | Low | Fine if the secret is long, random and never shipped in an app |
| The key ID looks like a path or a query | Low | Servers that look `kid` up without checking it can be steered to another key |
| Accepted before it was valid | Low | A request before `nbf` got a 2xx |
| No audience | Low | No `aud` on an access token sent to more than one site |
| Personal data in the payload | Info | The user's email or phone, readable by every host that receives the token |
| Also sent outside the scope | Info | With an active scope: hosts outside it received the token |

## Sign-in flows

*[Screenshot: The Sign-in Flows tab. The list shows Authorization code with PKCE on auth.example.com, a Token refresh, and an Implicit flow on shop.example.com with a red mark. The implicit flow is open: client web-shop, its redirect URI, findings for Implicit flow, No state parameter and Tokens in the redirect's fragment, a Timeline with the authorization request (response_type token, no state) and the redirect back to the app (access_token in the fragment), each with its status, time and a Show link, and the access token it issued.. A legacy implicit sign-in: every hop on one timeline, and what's wrong with it.]*

Each flow lists its client, redirect URI and scopes, its findings, the timeline (each step with its status, time, what it carried, such as `state`, `PKCE S256`, `code` or `issued access_token, id_token`, and **Show** to select the request), and the tokens it issued.

Cloak Proxy recognizes the authorization request by its `client_id` and `response_type`, the redirect back by its `state` (or the redirect URI) whether it's a request to the app or the `Location` of a sign-in response, and the token request by its `code`. Native apps that receive the redirect on a custom scheme (`com.example.shopapp://callback`) still show it, from the `Location` header. Token requests on their own (refresh, client credentials, password, device code, token exchange) are grouped per grant, client and endpoint.

### Sign-in findings

| Finding | Severity | Why it matters |
|---|---|---|
| The state came back changed | High | The app must reject the redirect; check that it does |
| Tokens issued without the PKCE verifier | High | The code was requested with PKCE but the server didn't ask for the verifier |
| Implicit flow | High | Tokens come back in the redirect URL. Use the code flow with PKCE |
| Tokens in the redirect's query string | High | (In the fragment it's Medium.) |
| An authorization code worked twice | High | Codes must be single-use |
| Sign-in over plain HTTP | High | Part of the sign-in went unencrypted |
| No state parameter | Medium | Nothing ties the redirect to this sign-in (login CSRF). Low when PKCE is used |
| The state didn't come back | Medium | |
| No PKCE | Medium | An intercepted code can be redeemed by someone else. OAuth 2.1 requires PKCE for every client |
| PKCE with the plain method | Medium | Use `S256` |
| Redirect URI over plain HTTP | Medium | |
| OpenID Connect without a nonce | Medium | For response types that return an ID token in the redirect |
| Password grant | Medium | The app handles the user's password itself; OAuth 2.1 removes this grant |
| A refresh token worked more than once | Low | No rotation: a stolen refresh token keeps working |
| The sign-in ended with an error | Info | The `error` the authorization server returned |

> **Note:**
> The flow half of this page works from what's in the traffic. When the authorization server or the app's redirect isn't captured (a pinned connection, another device), the timeline shows what is, and checks that need a missing hop don't run.

## Findings

The **Findings** tab lists every finding for tokens and flows by severity. Double-click one to open its token or flow; **Show Requests** selects the requests in the traffic list. The copy button at the top copies them all as Markdown, with token values masked, for a report.

*[Screenshot: The Findings tab: a High section with Implicit flow, Sent over plain HTTP and Unsigned token (alg none), then a Medium section with Never expires, No state parameter, Sent in a URL, Sent to 2 sites, Sent to a host its audience doesn't name and Tokens in the redirect's fragment, each with the token or flow it's about, its explanation and a Show Request button.. Every finding, worst first.]*

With a [scope](https://docs.subcloak.com/cloak-proxy/security/scope/) active, **In scope** at the top looks only at in-scope requests.

## AI assistants and the command line

The MCP tool `inspect_tokens` returns the same: a summary, the tokens, the flows or the findings (`section`), or one token or flow in full (`id`, with `trace: true` for its Trace Value map). Values are always masked, and token ids are fingerprints, never the token.

```sh
cloakctl tokens                      # counts and the worst findings
cloakctl tokens --section findings   # every finding as JSON
cloakctl tokens tok-3f2a… --trace    # one token in full, with everywhere it went
```

> **On Linux:**
> (added in Cloak Proxy 0.5.1) There's no Tokens and Sign-in window in the Linux app yet. The checks run in the engine: use `cloakctl tokens` or the `inspect_tokens` MCP tool.

## Related

  - [Scope](https://docs.subcloak.com/cloak-proxy/security/scope/): Mark each place a token went as in or out of scope.
  - [Trace Value](https://docs.subcloak.com/cloak-proxy/inspect/trace-value/): Where a value came from and every request it went into.
  - [Inspector](https://docs.subcloak.com/cloak-proxy/inspect/inspector/): The Auth tab decodes one request's token.
  - [Findings and reports](https://docs.subcloak.com/cloak-proxy/security/findings/): Add a token finding to an engagement's report.
