# Authorization response modes

Choose how M7 delivers an authorization-code result to your application.
`response_type=code` selects the result type; `response_mode` selects its
delivery. Every mode below carries a code or an authorization error. Access,
refresh and ID tokens are obtained afterward from the token endpoint.

## Register a list, request one mode

The M7 registration extension `response_modes` is an allowlist. An application
may register several modes, then send exactly one case-sensitive
`response_mode` on each authorization request. It must be both registered for
that client and advertised by live discovery.

Omitting the request parameter selects `query`; it does not select the first
registered mode. If the client allows only `form_post`, it must explicitly
request `form_post`. An unsupported or disallowed mode returns a local
`invalid_request`; M7 does not fall back to another delivery method.

For example, this JSON is the response-mode portion of a
[registration request](https://m7.org/docs/api/sso.user.m7.org/registration.md):

```json
{
  "response_modes": ["query", "form_post.jwt", "web_message.opener"],
  "authorization_signed_response_alg": "RS256"
}
```

Use [API User client management](https://m7.org/docs/api/api.user.m7.org/oauth-clients#configure-authorization-response-modes)
to change an existing client. Registration omission or `[]` selects
`["query"]`. An omitted management update preserves an existing nonempty
list; authorized reads and writes fill an empty stored list with `query`.

Send the selected mode with the ordinary authorization parameters, in the
body submitted to PAR, or inside the signed Request Object for JAR. PAR and
JAR preserve the selected mode with the original transaction; an outer browser
parameter cannot replace it. JAR Request Objects at `/par` remain unsupported.

## Delivery table

| Requested mode | Delivery | Response location |
| --- | --- | --- |
| `query` | Browser redirect | Ordinary fields in the callback query |
| `form_post` | Automatically submitted form, `POST`, `application/x-www-form-urlencoded` | Ordinary fields in the form body |
| `fragment` | Browser redirect | Ordinary fields in the callback fragment after `#` |
| `query.jwt` | Browser redirect | One query field, `response`, containing signed JARM |
| `form_post.jwt` | Automatically submitted form POST | One body field, `response`, containing signed JARM |
| `fragment.jwt` | Browser redirect | One fragment field, `response`, containing signed JARM |
| `jwt` | Default JARM delivery for code flow | Same transport as `query.jwt` |
| `web_message.opener` | Browser `postMessage` | Response object to the opening window |
| `web_message.parent` | Browser `postMessage` | Response object to the embedding parent |
| `web_message` | Browser `postMessage` | Opener when present; otherwise the embedding parent |

Every callback uses the validated registered HTTPS `redirect_uri`. Keep it
free of fragments and reserved response parameter names. Safe existing query
parameters are preserved. The same selected mode delivers success and safely
returnable errors, including cancellation. See
[callback outcomes](https://m7.org/docs/api/sso.user.m7.org/authorization-callback-outcomes.md) for the field and
error contract.

### Form and fragment callbacks

For form POST, accept the expected form content type at the registered
callback and validate the original transaction. A cross-site form POST may
omit a `SameSite=Lax` session cookie. Your callback architecture must recover
the original transaction safely; missing cookies never justify skipping state
validation. The current M7 Web SDK uses a same-origin browser relay before
continuing its existing callback processing.

A fragment is never sent to the callback server. Browser code must read it,
remove it from the visible URL, and pass it to the application's trusted
callback processor. Handle the result only through the transport saved for
the transaction. Reject mixed query/body/fragment inputs and duplicate
protected fields. Keep callback and relay pages non-cacheable and free of
third-party scripts and analytics.

## Signed authorization responses: JARM

M7 implements signed [JARM](https://openid.net/specs/oauth-v2-jarm-final.html).
The four JARM modes carry one compact JWS in `response` by default. Registered
response encryption instead requires a signed-then-encrypted compact JWE. JARM is a response
envelope, not an access token, ID token or client assertion.

`authorization_signed_response_alg` selects exactly `RS256` or `RS512` and
defaults to `RS256`. Dynamic Registration returns the effective value.
For an existing client, set it with API User's `set_knobs`; omission preserves
the current setting, while `null`, an empty string or `default` removes the
override and restores the RS256 default. This policy is independent of
ID-token signing, signed UserInfo, JAR and client authentication. Selecting an
algorithm does not itself enable a JARM response mode.

| JARM field | M7 contract |
| --- | --- |
| Protected `alg` and `kid` | Registered RS256/RS512 algorithm and provider signing-key identifier |
| `iss` | Exact authorization-server issuer, `https://sso.user.m7.org` |
| `aud` | Exact public OAuth `client_id`, not a resource API audience |
| `iat`, `exp` | Numeric issuance and expiry; M7 issues a five-minute envelope |
| `state` | Original transaction state when available |
| `code` | Successful authorization only |
| `error` and optional error fields | Authorization failure only, including M7 `error_reason` and optional `trace_id` |

Validate the signature using the expected provider's discovered JWKS, exact
`kid`, and the algorithm saved for this client. Do not obtain keys from URLs
inside an untrusted JWT. Require the exact issuer and client audience, valid
time claims, matching single-use state, and exactly one terminal result.
Only then may the application use the code or process the error. Reject
unsigned, tampered, expired, replayed, wrong-client and mixed plain/JARM
responses. Never accept unsigned outer fields as replacements for JWT claims.

`authorization_signing_alg_values_supported` advertises the two JARM
signing algorithms. Registered `authorization_encrypted_response_alg=RSA-OAEP-256`
and `authorization_encrypted_response_enc=A256GCM` require encryption for
successes and eligible errors across all four JARM modes. Plain modes, including
an omitted mode that would default to `query`, are rejected for those clients.
SSO emits only the encrypted result, without plaintext code/state siblings;
key or signer failures produce a local error with no sensitive redirect.
Cancellation tickets retain the policy for up to ten minutes and resolve remote
keys when followed. This runtime is locally tested; deployment and SDK
product decryption remain pending. See [response encryption](https://m7.org/docs/api/sso.user.m7.org/response-encryption.md).

## Web Message: popup and iframe

M7's three Web Message modes follow the
[Web Message response-mode draft](https://datatracker.ietf.org/doc/html/draft-meyerzuselha-oauth-web-message-response-mode-01).
This is a draft extension, not a claim of finalized OAuth standardization.
The provider sends a JavaScript object with string values: the ordinary code
or error fields, state, and `iss=https://sso.user.m7.org`. These modes do not
wrap the result in a JARM JWT.

The provider uses the validated full redirect URI as the `postMessage` target;
the browser derives the receiving origin. The redirect remains a registered
security boundary even though the provider does not navigate the secondary
window to it.

1. Retain the expected issuer, mode, state, PKCE verifier, nonce and expiry in
   the application's original transaction.
2. Install the message listener before opening the popup or loading the frame.
   Open a popup from a user gesture and retain its exact window reference;
   for an iframe, retain its `contentWindow`.
3. Require `event.origin` to equal the expected provider origin, `event.source`
   to equal that exact window, and the message's `iss` and state to match the
   transaction. Reject malformed objects and conflicting result fields.
4. Accept only one matching result, remove the listener, and close the popup
   or remove the frame. Continue code exchange in the original application
   context; an error must not start token exchange.
5. On blocked/closed windows or expired transactions, offer a fresh sign-in.
   Do not bypass the window, origin or state checks to recover.

Explicit opener and parent modes use only their named receiver. Generic
`web_message` prefers an opener; it uses a parent only when no opener exists.
A missing, self or closed receiver fails; a closed opener does not cause a
silent switch to a parent. Popup settings that sever the opener relationship,
including `noopener` or incompatible cross-origin isolation, prevent delivery.

### Iframe prerequisites and current limits

The embedding origin needs provider approval in the applicable CSP
`frame-ancestors` policy, and the application must permit the provider in its
own `frame-src`. Registering `web_message.parent` does not grant permission to
frame M7. The current provider allowance is restricted to approved test origins;
coordinate an exact-origin allowance before using an iframe on another site.

Cross-site cookie restrictions can prevent remembered-account reuse. The live
iframe checks completed fresh password sign-in and cancellation; they did not
establish silent or remembered-session sign-in. MFA, email sign-in, upstream
federation and consent denial were not exercised by those iframe checks and
may need additional framing and browser acceptance. Use a top-level or popup
flow when the required hosted step cannot operate inside the frame.

### M7 Web SDK configuration

Response-mode-aware Web SDK source accepts `M7_RESPONSE_MODE` with any of the
ten names. For JARM, `M7_AUTHORIZATION_SIGNED_RESPONSE_ALG` must match the
client's RS256/RS512 policy. `M7_AUTH_USE_PAR` selects PAR or direct
authorization. The generic launcher offers popup and embedded sign-in;
explicit parent/opener modes offer only their matching presentation.

These capabilities were tested from the updated source package. Existing
immutable SDK downloads are not changed by this provider update; use a package
that explicitly includes the required callback support.

Device Authorization and `client_credentials` do not use an authorization
callback, so response modes do not change their token delivery.
