Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,47 @@ allowed-user-backends::
Limit the users which are allowed to login to a specific user backend - e.g. LDAP
(`'allowed-user-backends' ⇒ ['LDAP']`)

audience::
The value your identity provider puts into the access token's `aud` (audience) claim. Once set,
an access token is accepted only if `aud` names it, and no other claim is used to identify
ownCloud. Takes a single non-empty string or a list of them, and *replaces* `client-id` as the
expected value rather than adding to it. `null` counts as not set; any other unusable value - a
number, a boolean, an empty string, an empty list - is dropped with a warning naming it, and if
nothing usable is left every access token is rejected, so a typo here fails closed.
+
Available starting app version 2.3.5, which is also the release that introduces the audience
check on this server line - every released version up to and including 2.3.4 does not check the
audience at all. Without the key, 2.3.5 accepts an access token whose `aud` carries the
`client-id`, and also one whose `azp`, `appid` or `client_id` claim does - which is what keeps
providers working that put the *resource server* into `aud`, as RFC 9068 section 3 defines it.
The one shape the upgrade can lock out is an introspection response carrying neither `aud` nor
`client_id`, which RFC 7662 permits and which no `audience` value can rescue. Which provider
sends what is documented at
https://doc.owncloud.com/server/10.16/admin_manual/configuration/user/oidc/oidc.html#access-token-audience
+
Microsoft ADFS is one of the providers that names the resource: it prefixes the identifier of
the application the token was issued for with `microsoft:identityserver:`, unless that
identifier is already a URL, in which case it is sent verbatim. Read the identifier with
`Get-AdfsWebApiApplication` for an OpenID Connect application group registration, or with
`Get-AdfsRelyingPartyTrust` for a legacy WS-Federation or SAML relying party trust. It is often
configured to the same GUID as the `client-id`, which is why the prefixed value frequently
repeats it - but it is a *resource* identifier, not the client, and the two are free to differ.
Whatever your provider sends has to be reproduced here exactly, including case: the comparison is
strict, so a difference in casing, or a numeric `aud` against a string here, will not match.
+
Setting it binds tokens to the *resource*: one that ownCloud's own client obtained for a
different resource of the same provider - through an RFC 8707 `resource` parameter or an RFC 8693
token exchange - stops being accepted. So do ID tokens, but only where the configured value
differs from the `client-id`, since an ID token's `aud` is the `client-id` by definition. What it
does not bind is the *client*: any token whose `aud` names ownCloud is accepted whichever client
requested it. So choose a value only ownCloud can be issued a token for, do not reuse a
tenant-wide resource identifier here, and control in the provider which clients may ask for it.
+
Do not set the key at all if your token introspection response omits `aud`, which RFC 7662
permits, because every opaque token would then be rejected. And with
`exchange-token-mode-before-introspection`, the first usable entry is also the audience the
token exchange requests, so list the resource ownCloud should be given first.

auth-params::
Additional parameters which are sent to the IdP during the auth requests

Expand All @@ -421,6 +462,20 @@ auto-update::
When using the provisioning mode `auto-update`, user account info will update with the current
information provided by the OpenID Connect provider upon each user log in.

exchange-token-mode-before-introspection::
If set, an RFC 8693 token exchange is performed before a token is introspected, and the
exchanged token is what gets verified. The value selects the subject token type: `refresh-token`
uses `urn:ietf:params:oauth:token-type:refresh_token`, and `access-token` - which is also what
any other value falls back to - uses `urn:ietf:params:oauth:token-type:access_token`. The
subject is taken from the OpenID Connect login session while a session is being verified, so it
requires such a session: on a request authenticated by a bearer token alone there is none, and
the exchange is attempted with an empty subject token and fails. When user information is read
the subject is the client's current refresh or access token instead, and only if
`use-access-token-introspection-for-user-info` is set as well. The audience requested for the
exchanged token is the first usable entry of `audience`, the `client-id` when `audience` is not
set, and none at all when `audience` is set but holds nothing usable. Use one of the two values
or leave the key out: an empty or otherwise falsy value is not treated alike on both paths.

insecure::
Boolean value (`true`/`false`), no SSL verification will take place when talking to the
IdP - **DO NOT use in production!**
Expand All @@ -440,6 +495,20 @@ mode::
This is the attribute in the owncloud accounts table to search for users.
The default value is `email`. The alternative value is: `userid`.

ocis-routing-policy-claim::
For setups that run ownCloud Classic and ownCloud Infinite Scale side by side behind a proxy:
the user information claim that holds the routing policy. After a successful login, when the
claim is present, its value is written to the cookie named by `ocis-routing-policy-cookie` so
that the proxy can route the user to the right backend. The default is `ocis.routing.policy`.

ocis-routing-policy-cookie::
Name of the cookie the value of `ocis-routing-policy-claim` is written to. The default is
`owncloud-selector`.

ocis-routing-policy-cookie-directives::
Directives appended to the routing policy cookie, for example to scope or to secure it. The
default is `path=/;`.

post_logout_redirect_uri::
A given URL where the IdP should redirect to after logout.

Expand Down Expand Up @@ -472,9 +541,19 @@ Client ID to be used with the token introspection endpoint.
token-introspection-endpoint-client-secret::
Client secret to be used with the token introspection endpoint.

use-access-token-introspection-for-user-info::
If set to `true`, user information is read from the token introspection response instead of from
the userinfo endpoint, using `token-introspection-endpoint-client-id` and
`token-introspection-endpoint-client-secret` and honouring
`exchange-token-mode-before-introspection`. `use-access-token-payload-for-user-info` is
evaluated first and wins for a JWT access token; an opaque token has no payload to read, so the
introspection response is used even when both keys are set.

use-access-token-payload-for-user-info::
If set to `true` any user information will be read from the access token.
If set to `false` the userinfo endpoint is used (starting app version 1.1.0).
If set to `false` the userinfo endpoint is used (starting app version 1.1.0) - unless
`use-access-token-introspection-for-user-info` is set, which takes the introspection response
instead.

=== Easy setup

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,41 @@ Use these links to see the corresponding configuration section for:
],
----

[NOTE]
====
Which token version your app registration issues decides what ends up in the access token's
`aud` (audience) claim. It is not part of the screens above, but the `requestedAccessTokenVersion`
property in menu:Manage[Manifest]:

* `null` or `1` - the default - issues v1.0 tokens, whose `aud` is the *Application ID URI* you
set under menu:Expose an API[], `api://YOUR-APPLICATION-ID-SCOPE-URI` below.
* `2` issues v2.0 tokens, whose `aud` is the application's *client id*.

Both work without further configuration on this server line: openidconnect 2.3.4 and earlier do
not check the audience at all, and 2.3.5 - in preparation - accepts the claim naming the client as
well, `appid` for v1.0 tokens and `azp` for v2.0. To have the audience enforced instead, add the
matching value:

[source,php]
----
'openid-connect' => [
// ...
// v1.0 tokens (the default): the Application ID URI exactly as set under
// "Expose an API" - note this is the App ID URI itself, without the /owncloud scope
'audience' => 'api://YOUR-APPLICATION-ID-SCOPE-URI',
// v2.0 tokens: the client id
// 'audience' => 'YOUR-CLIENT-ID',
],
----

Take the value from menu:Expose an API[Application ID URI] verbatim: setting `audience` also
switches off the claim that names the client - `appid` in v1.0 tokens, `azp` in v2.0 - so a value
that does not match exactly rejects every access token. Decode one access token and read its `ver` claim to see which version your tenant issues.
See
xref:configuration/user/oidc/oidc.adoc#access-token-audience[Access Token Audience] for the
details and for the openidconnect versions this applies to.
====

If you want to let ownCloud create users which are not present during a OIDC authentication, replace +
`'auto-provision' => ['enabled' => false],` with:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,170 @@ IMPORTANT: If you use the `.htaccess` file in the ownCloud web root, you have to

. Once service discovery is available as described above, the ownCloud clients will attempt to connect via OpenID Connect.

[#access-token-audience]
== Access Token Audience

An access token is accepted only if it names ownCloud. On this server line that check arrives with
openidconnect **2.3.5**, which is in preparation:

* every released version up to and including **2.3.4** does not check the audience at all;
* **2.3.5** accepts a token whose `aud` (audience) claim carries the configured `client-id`, or
whose `azp`, `appid` or `client_id` claim does, and adds the `audience` parameter for declaring
what your provider really puts in `aud`. It checks both JWT access tokens and opaque ones
verified through a token introspection endpoint, where the response's `client_id` counts the
same way.

Both halves therefore arrive together here, which is why the upgrade needs no configuration change
for any provider whose shape is known - see the table. An access token's `aud` identifies the
*resource server* the token was minted for (RFC 9068 section 3), not the client that asked for it,
so many providers never put the ownCloud `client-id` there; accepting the client-naming claim is
what keeps those working.

[NOTE]
====
The ownCloud 11 line got the two halves in separate releases: openidconnect 2.4.1 checks the
audience and has no way to override it, so a provider that does not put the `client-id` into `aud`
cannot authenticate at all there, and 2.4.2 is the fix. If you also run an ownCloud 11 instance on
2.4.1, that is where "login works, the first click logs me out" comes from.
====

[width="100%",cols="17%,30%,27%,26%",options="header"]
|===
| Identity provider
| What lands in the access token's `aud`
| On openidconnect 2.3.4 and earlier
| On openidconnect 2.3.5

| Keycloak ^1^
| Nothing - there is no `aud` claim unless an audience mapper is configured for the client. `azp`
carries the client id.
| Works as it is - the audience is not checked.
| Works as it is. `audience` cannot be used at all here - with no `aud` claim, no value can match.

| Microsoft Azure AD / Entra ID, v1.0 tokens ^2^
| The Application ID URI, for example `api://<client-id>`. There is no `azp`; the client is named
by `appid`. This is the default, see the
xref:configuration/user/oidc/ms-azure-setup.adoc[Azure setup page].
| Works as it is - the audience is not checked.
| Works as it is. Set `audience` to the Application ID URI to bind tokens to ownCloud's API.

| Microsoft Azure AD / Entra ID, v2.0 tokens ^2^
| The resource application's client id, which is ownCloud's own `client-id` in the setup described
here. `azp` carries it as well.
| Works as it is - the audience is not checked.
| Works as it is. `audience` may be set to the `client-id`.

| Microsoft ADFS ^2^
| The relying party identifier, rendered as `microsoft:identityserver:<identifier>` unless the
identifier is already a URL. `appid` carries the client.
| Works as it is - the audience is not checked.
| Works as it is. Set `audience` to that exact value, including case, to bind tokens to ownCloud.

| Kopano Konnect ^3^
| The client id.
| Works as it is - the audience is not checked.
| Works as it is.

| OneLogin, with API authorization ^2^
| The configured API audience URI or URIs, never the client id. `azp` carries the application id.
| Works as it is - the audience is not checked.
| Works as it is. Set `audience` to the API audience URI to bind tokens to that API.

| PingIdentity PingFederate, cidaas
| Depends on how the access token is configured in the product - not verified by ownCloud.
| Works as it is - the audience is not checked.
| Decode one access token: it works as it is if `aud` or a client claim names ownCloud, otherwise
set `audience` to what `aud` holds. This is the one row where the upgrade can need a change.
|===

(1) Verified against Keycloak 26.0 with a confidential client and no audience mapper. +
(2) From the provider's own documentation and from tokens reported by administrators. +
(3) From the Konnect sources, which pass the client id as the access token audience.

=== What the Log Says

A rejected audience is logged with both values:

----
Token audience does not match the expected audience: token "aud" is "...", expected one of ["..."]
----

The value reported as `token "aud"` is what your provider sends, and therefore exactly what
belongs in `audience` if you decide to set it.

From 2.3.5, a token accepted on its client-naming claim while carrying an audience you *could*
have configured - Azure AD v1.0 tokens, ADFS, OneLogin - is recorded once per verified token with
the same advice:

----
Access token "aud" does not name this relying party, accepted because "appid" matches the
configured client-id. To have the audience enforced, set the openid-connect "audience" config
key to ...
----

That warning is expected for those providers and does not indicate a broken setup; setting
`audience` is what ends it. It is not written when the token carries no audience at all, as with
Keycloak, because then there is nothing to put in the key - such a token is still accepted on its
`azp` claim, unless `audience` is configured, in which case it is rejected and logged as a
mismatch like any other.

Independently of the audience, and from 2.3.5, a token that declares itself *not* to be an access
token is refused with `Token is not an access token`: `typ` of `Refresh` or `Offline` (Keycloak),
`token_use` of `refresh` (AWS Cognito).

ID tokens carry no such marker, and an ID token's `aud` *is* the `client-id` by definition, so one
presented as a bearer token is accepted for as long as the `client-id` is an accepted audience.
Setting `audience` to anything else - which is exactly what ADFS, Azure AD v1.0 tokens and
OneLogin need - rejects ID tokens as a side effect. Where that is not an option, treat ID tokens
as credentials.

=== When to Set the audience Parameter

None of the providers above needs it on 2.3.5, with the one exception noted in the table. What it adds is a binding to the
*resource*: with it set, a token that ownCloud's own client obtained for some *other* resource of
the same provider - through an RFC 8707 `resource` parameter, or RFC 8693 token exchange - no
longer authenticates here. Set it if your provider issues tokens to ownCloud's client for more
than one resource.

What it cannot do is keep *another* client's tokens out. Any token whose `aud` names ownCloud is
accepted, whichever client requested it, and a client of the same provider can often be granted
exactly that - an audience mapper of its own, or a `resource` parameter naming ownCloud. That is
the resource-server model, it is the same before and after setting `audience`, and the only place
to control it is the provider: decide there which clients may be issued tokens for ownCloud.

See
xref:configuration/server/config_apps_sample_php_parameters.adoc#app-openid-connect-oidc[the parameter reference]
for the exact semantics of the key.

[#making-keycloak-send-an-audience]
=== Making Keycloak Send an Audience

Keycloak leaves `aud` out of the access token unless the client has an audience mapper. Adding one
makes Keycloak send the client id. Nothing on this server line requires it: no released version
checks the audience, and 2.3.5 accepts Keycloak's `azp` without a mapper. Add it only if you want
to set `audience` afterwards and have the audience enforced.

In the Keycloak admin console, go to menu:Clients[your ownCloud client > Client scopes] and open
the dedicated scope named `<client-id>-dedicated`, then
menu:Add mapper[By configuration > Audience] and set *Included Client Audience* to your ownCloud
client, leaving *Add to access token* on.

The same through `kcadm.sh`, with `CLIENT_UUID` the internal id from
`kcadm.sh get clients -r <realm> -q clientId=<client-id> --fields id`:

[source,bash]
----
kcadm.sh create clients/CLIENT_UUID/protocol-mappers/models -r <realm> \
-s name=oc-audience \
-s protocol=openid-connect \
-s protocolMapper=oidc-audience-mapper \
-s 'config."included.client.audience"=<client-id>' \
-s 'config."access.token.claim"=true'
----

Afterwards the access token carries `"aud": "<client-id>"`, and `audience` may be set to the same
value to have it enforced.

== General Example Setup

All IdPs have their own setup, but often share common ways of configuring things. Although not identical, the xref:example-setup-using-kopano-konnect[Kopano Konnect] example may be a good starting point for the specific configuration of your setup. As Microsoft with Azure AD is different, it has its own example section.
Expand Down
Loading
Loading