# Upgrading from 1.1.0-rc.13 to 1.1.0-rc.14

This release comes from a self-run of the OpenID Foundation conformance suite against every OpenID Connect OP
certification profile ([OpenID Connect conformance-suite run](../compliance/oidc-conformance.md)). Most of it is new and off until you use it. Two
changes reach every installation: tokens now carry the standard profile claims only for the scopes granted, and
userinfo no longer returns the access token's own claims. Check your relying parties for those two before you upgrade.

## Before you upgrade (summary)

- **Migrations V192 to V195** add nullable columns to `service_provider_oauth`: `jwks` (V192); `logo_uri`,
  `policy_uri`, `tos_uri`, `userinfo_signed_response_alg`, `request_uris` (V193); `session_management` (V194);
  `initiate_login_uri` (V195). **Migration V196** adds a change-notification trigger on `realm_provisioning_config`.
  All catalog-only: no table rewrite, no value touched.
- **Relying parties that use the email address, name or other profile claims must ask for the scope** (below). Most
  do already; check the ones that send only `scope=openid`.
- **Relying parties that read token claims from userinfo** (`iss`, `aud`, `exp`, `scope`, `client_id`, `sid`, `acr`,
  `amr`, ...) must read them from the ID token or the access token instead (below).
- **Nothing else changes until you configure it:** the implicit and hybrid flows and session management are off for
  every client, and the new client fields are empty.

## Tokens carry profile, email, address and phone claims only for the scopes granted

- **What changed.** An ID token or access token carries the claims of the `profile`, `email`, `address` and `phone`
  scopes (OpenID Connect Core 1.0 §5.4) only when that scope was granted, or when the claim is named in the `claims`
  request parameter (§5.5) for that token's channel (`id_token` for the ID token, `userinfo` for the access token) and
  belongs to a scope the client is registered for and that was granted: by the user's consent, or, for a client
  without a consent screen, by its registration. Before, every token carried the user's whole standard profile
  whatever was requested. Userinfo is built from the access token, so it follows the same rule. Unchanged:
  `realm_access`, `resource_access`, organizations, claims released by protocol mappers, workload-identity tokens and
  the tokens a client or agent gets as itself (`client_credentials`).
- **Who is affected.**
  - Relying parties that request only `openid` and read `email`, `name`, `preferred_username` or other profile
    claims: those claims are now absent.
  - Token-exchange on-behalf-of tokens: they carry these claims only when their scope includes the scope the claims
    belong to.
  - The admin console: its `helix-console` client was seeded with `openid,profile`. It is now seeded with
    `openid,profile,email`, the server adds `email` to the existing `helix-console` client of every realm at start-up,
    and the console requests `openid profile email`. Nothing to do.
- **What to do.** Make each relying party request the scopes whose claims it uses, typically `openid profile email`
  (many already do, for example Nextcloud `user_oidc`), and make sure each client is registered for those scopes.
  For token exchange, request the scope in the exchange.

## Userinfo returns claims about the end-user only

- **What changed.** Userinfo (OpenID Connect Core 1.0 §5.3.2) returns `sub`, the released standard claims,
  `claims`-parameter claims, `realm_access`, `resource_access`, organizations, verified claims and the BSN where
  released. It no longer returns the access token's own claims: `iss`, `aud`, `exp`, `iat`, `nbf`, `jti`, `scope`,
  `client_id`, `sid`, `azp`, `auth_time`, `nonce`, `acr`, `amr`, `cnf`, `act`, `may_act`. For a client with
  `userinfoSignedResponseAlg` set, the response is a JWT (`application/jwt`) signed with the realm's ID-token key at
  that algorithm, with `iss` and `aud` (the client) besides the end-user claims; for every other client it stays JSON.
- **Who is affected.** Relying parties or gateways that read token metadata (expiry, scopes, session id, `acr`/`amr`)
  from the userinfo response. `realm_access` is still there.
- **What to do.** Read those values from the ID token (`sid`, `auth_time`, `acr`, `amr`) or from token introspection
  (`exp`, `scope`, `client_id`).

## Standard claims in their JSON types

- **What changed** (OpenID Connect Core 1.0 §5.1, §5.1.1): `updated_at` is a number (seconds since the epoch),
  `phone_number_verified` a boolean, and `address` one JSON object with `formatted`, `street_address`, `locality`,
  `region`, `postal_code` and `country`. Before, `updated_at` and `phone_number_verified` were strings and the address
  members were flat claims (`address.locality`, ...). A stored value that is no such value is left out rather than
  sent mistyped.
- **Who is affected.** Code that parsed `updated_at` or `phone_number_verified` as strings, or read `address.*` as
  separate claims.
- **What to do.** Read them as the specification types them; read the address members from the `address` object.

## Dynamic Client Registration defaults and metadata

- **What changed.**
  - A registration without `scope` gets `openid`, plus `offline_access` for a client of the `refresh_token` grant
    (RFC 7591 §2 lets the server choose the default). Before, such a client got no scope, and its `scope=openid`
    request failed with `invalid_scope`.
  - A response that issues a `client_secret` carries `client_secret_expires_at: 0` (the secret does not expire; RFC
    7591 §3.2.1); without a secret it is absent.
  - Accepted and returned: `jwks` (mutually exclusive with `jwks_uri`), `id_token_signed_response_alg`,
    `userinfo_signed_response_alg`, `logo_uri`, `policy_uri`, `tos_uri`, `request_uris` and `initiate_login_uri`.
  - Response types with `id_token` or `token` need the `implicit` grant, which registration may add only when the
    realm administrator allows that grant for registration (never by default).
- **Who is affected.** Clients that register themselves. A client registered without `scope` that needs more than
  `openid` (and `offline_access`) gets nothing further by default.
- **What to do.** Nothing for existing registrations. A registering client that needs `profile` or `email` sends them
  in `scope`.

## New client settings, all off or empty by default

- **What changed.** Per client, in the admin API, Dynamic Client Registration, the realm export/import and the
  console ([OpenID Connect flows](../integration/oidc-flows.md) and [OIDC client settings](../manage/oidc-client-settings.md)):
  - **The implicit and hybrid flows** (`id_token`, `id_token token`, `code id_token`, `code token`,
    `code id_token token`): the `implicit` grant type, off by default and only together with `authorization_code`.
    Refused (`400`, field `grantTypes`) for a client with a FAPI setting (a required signed request object,
    certificate-bound access tokens, a JARM mode) and in a realm with the HIGH key-custody profile. The HIGH rule
    holds at use too: in a HIGH realm the authorization endpoint never answers an implicit or hybrid request, whatever
    a client has stored, and a move to HIGH is refused (`409`, code `implicitFlowClients`, naming the clients) while
    a client of the realm has the `implicit` grant. An ID token from the authorization endpoint needs a `nonce`; an
    access token from it lives at most 5 minutes and never comes with a refresh token, and a second use of a hybrid
    flow's code revokes it with the code's other tokens.
  - `jwks` (a JWK Set by value, instead of `jwksUrl`), `logoUri`, `policyUri`, `tosUri`, `userinfoSignedResponseAlg`,
    `requestUris` (request objects by reference, fetched only from a registered URI), `sessionManagement`
    (`session_state` and the check-session iframe, off by default) and `initiateLoginUri`.
- **Who is affected.** Nobody until a setting is used.
- **What to do.** Turn the implicit or hybrid flow on only for a relying party that cannot use the code flow, and put
  the proxy setting below in place first.

## Discovery states more

- **What changed.** `/.well-known/openid-configuration` now lists:
  - `scopes_supported`: `openid`, `profile`, `email`, `address`, `phone`; and `address` in `claims_supported`;
  - `request_parameter_supported`, `request_uri_parameter_supported` and `require_request_uri_registration`, all
    `true`, with `request_object_signing_alg_values_supported` (`RS256`);
  - `userinfo_signing_alg_values_supported` (those of `RS256`, `PS256`, `ES256` the realm publishes ID-token keys
    for);
  - `response_modes_supported`: `query` and `form_post`, plus `fragment` in a realm that advertises the implicit and
    hybrid flows (`response_mode=form_post` is now honoured for every response type, errors included; before it was
    ignored);
  - `frontchannel_logout_supported` and `frontchannel_logout_session_supported`: `true`;
  - `check_session_iframe`, only in a realm where a client uses session management;
  - the implicit and hybrid response types and the `implicit` grant, only in a realm where a client has that grant or
    registration may add it.
- These per-realm facts come from the configuration cache (V196 keeps it current), so a warm discovery request still
  reads nothing from the database.
- **Who is affected.** Relying parties that configure themselves from discovery; they may now choose `form_post` or
  request objects by reference.
- **What to do.** Nothing.

## Implicit and hybrid responses need a larger proxy response-header buffer

- **What changed.** Implicit and hybrid responses carry the ID token and access token in the `Location` header of the
  redirect. With many claims that header exceeds the 4 KB default response-header buffer of ingress-nginx (4 to 8 KB
  in other proxies), and the proxy answers `502`.
- **Who is affected.** Installations that turn the implicit or hybrid flow on for a client.
- **What to do.** Behind ingress-nginx, set the annotation `nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"`
  (chart: `ingress.annotations."nginx\.ingress\.kubernetes\.io/proxy-buffer-size"="32k"`); behind nginx,
  `proxy_buffer_size 32k; proxy_buffers 8 32k;`. The chart does not set it by default.

## A client assertion without `client_id` is accepted

- **What changed.** A `private_key_jwt` or `client_secret_jwt` request that leaves out `client_id` (OPTIONAL with an
  assertion, RFC 7521 §4.2) is accepted on every endpoint that authenticates clients: the client is the assertion's
  `iss` when it equals its `sub` (RFC 7523 §3). Before, it was refused with `invalid_request`. The assertion is
  verified exactly as before (signature against the client's keys, audience, lifetime, single use); one that names no
  single client is `invalid_request`.
- **Who is affected.** Clients that send only the assertion.
- **What to do.** Nothing.

## See also

- [OpenID Connect flows, response modes and claims](../integration/oidc-flows.md)
- [OIDC client settings](../manage/oidc-client-settings.md)
- [OpenID Connect conformance-suite run](../compliance/oidc-conformance.md)
- [Upgrades & migrations](upgrades.md)
