Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(`provider.example.org` for IdP-side URLs, `app.example.org` for
application-side URLs) instead of invented registrable domains

### Deprecated

- The `response_type` default of `id_token` in `getAuthorizationUrl()`, together
with the `response_mode` default of `query`. That pair is the OIDC implicit
flow with the ID token delivered in the query string: OIDC Core §3.2.2.5
returns implicit-flow parameters in the fragment, so the `query` mode relies on
a provider extension to be readable server-side, and it puts a credential where
access logs and browser history can keep it. Pass
`'response_type' => 'code'` and exchange the code with `getIdToken()`. **The
default becomes `code` in 6.0**, so passing it explicitly now is
forward-compatible. No runtime deprecation notice is emitted, since the noise
would fall on consumers who cannot silence it except by making this change

### Documentation

- Named the storage contract for `state` and `nonce`: the caller persists both
Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,14 +187,36 @@ container that memoizes the service — the property holds whichever request wro
it last, which may belong to a different user. `generateNonce()` stores nothing,
and the state is best treated as though it did the same.

Note that the default response type and mode
is set in ```OpenIdConfigurationProvider.php```
##### Response type: pass `code` explicitly

`getAuthorizationUrl()` currently defaults to

```php
'response_type' => 'id_token',
'response_mode' => 'query',
```

which is the OIDC implicit flow with the ID token delivered in the query string.
**Pass `'response_type' => 'code'` and exchange the code with `getIdToken()`
instead.** The default will become `code` in 6.0; passing it explicitly now is
both the recommended flow and forward-compatible.

Two reasons to move:

* OIDC Core [§3.2.2.5](https://openid.net/specs/openid-connect-core-1_0.html)
specifies that implicit-flow parameters are returned in the _fragment_. A
fragment never reaches the server, so the `response_mode => 'query'` default
exists to make the ID token readable server-side — a provider extension
(Azure AD B2C supports it) rather than something the spec describes.
* That puts a credential in the query string, where it reaches web server access
logs and browser history.

[RFC 9700](https://www.rfc-editor.org/rfc/rfc9700.html) §2.1.2 recommends `code`
over response types that return tokens in the authorization response. Its
normative sentence names access tokens, so it does not literally cover a bare
`id_token` response — but `code` is the flow it points at, and the one
[openid-connect-bundle](https://github.com/itk-dev/openid-connect-bundle) uses.

#### Verify authorized requests

The authorization service will redirect the user back to the `redirectUri`. This
Expand Down
7 changes: 7 additions & 0 deletions src/Security/OpenIdConfigurationProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ public function getAuthorizationUrl(array $options = []): string
// Add default response_type and response_mode. The `scope` default is
// supplied by getDefaultScopes() via league's
// getAuthorizationParameters(), so it is not repeated here.
//
// DEPRECATED DEFAULT: `id_token` is the OIDC implicit flow, and the
// `query` response mode exists to make the token readable server-side —
// OIDC Core §3.2.2.5 returns implicit-flow parameters in the fragment,
// so this relies on a provider extension and puts a credential in access
// logs. Callers should pass `response_type => 'code'` and exchange via
// getIdToken(). The default becomes `code` in 6.0.
return parent::getAuthorizationUrl($options + [
'response_type' => 'id_token',
'response_mode' => 'query',
Expand Down
Loading