Skip to content
subcloakdocs

Security testing

Tokens and sign-in

Since 0.5.1 Requires Cloak Proxy 0.5.1 or later.

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.

Tools › Tokens and Sign-in… (⌥ ⌘ J) 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 decodes one request’s token; this window looks at all of them together, across requests.

One access token: where it came from, how long it has left, everywhere it went, and what's wrong.

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.

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

Trace Value runs 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….

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

A legacy implicit sign-in: every hop on one timeline, and what's wrong with it.

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.

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.

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

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.

Every finding, worst first.

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.

With a scope active, In scope at the top looks only at in-scope requests.

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.

Terminal window
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