The aud claim in OAuth tokens issued by Solid appears incorrect and is causing authentication errors when a client supplies the access token.
Added 2019-Feb-01:
Added a TLDR re-iteration of the essential problem which triggered this issue, and the proposed fix, here for those not wanting to read the complete thread (though I recommend it, there's a lot of good information courtesy of @dmitrizagidulin)
@RubenVerborgh @kjetilk @dmitrizagidulin: Please could you comment?
/cc @kidehen @smalinin
Manifestation
OpenLink are seeing error Token does not pass the audience allow filter when trying to bind a Solid pod to Virtuoso WebDAV using our WebDAV 'DET' functionality.
Recreation
I've been able to replicate the problem, eliminating Virtuoso in the process.
I signed into a local instance of OSDB using solid.openlinksw.com:8444, then while logged in, I retrieved the access token from OSDB's oidc-rp storage.
curl -kIH "Authorization: Bearer {access-token}" https://cmsblakeley.solid.openlinksw.com:8444/public/
HTTP/1.1 403 Forbidden
X-Powered-By: OpenLink Solid Server
Vary: Accept, Authorization, Origin
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, On-Behalf-Of, webid-tls
Allow: OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE
WWW-Authenticate: Bearer realm="https://solid.openlinksw.com:8444", error="access_denied", error_description="Token does not pass the audience allow filter"
Content-Type: text/html; charset=utf-8
Content-Length: 877
ETag: W/"36d-HJZUCkFVG9xNWz4IGHiCqJijAlc"
Date: Tue, 22 Jan 2019 17:05:34 GMT
Connection: keep-alive
Decoding the oauth token (a JWT) using https://jwt.io/
{
"iss": "https://solid.openlinksw.com:8444",
"sub": "https://cmsblakeley.solid.openlinksw.com:8444/profile/card#me",
"aud": "79888c62ca35e4b89665f328b494600b",
"exp": 1549385146,
"iat": 1548175546,
"jti": "5709a267c48596e8",
"scope": "openid profile email"
}
The "aud" (Audience) claim matches the clientId under key rpConfig.session.clientId in the oidc-rp data.
Problem Source
(Line numbers refer to OpenLink's forks)
The error comes from: AuthenticatedRequest#allowAudience
@solid/oidc-rs/src/AuthenticatedRequest.js:354
allowAudience (request) {
...
if (typeof audience === 'function') {
if (audience(aud)) {
return // token passes the audience filter test
} else {
return request.forbidden({
realm,
error: 'access_denied',
error_description: 'Token does not pass the audience allow filter'
})
}
}
...
Function audience above is a reference to OidcManager#filterAudience
@solid/oidc-auth-manager/src/oidc-manager.js:251
initRs () {
let rsConfig = { // oidc-rs
defaults: {
handleErrors: false,
optional: true,
query: true,
realm: this.providerUri,
allow: {
// Restrict token audience to either this serverUri or its subdomain
audience: (aud) => this.filterAudience(aud)
}
}
}
this.rs = new ResourceAuthenticator(rsConfig)
}
The filter function which is rejecting the aud claim is defined at:
@solid/oidc-auth-manager/src/oidc-manager.js:441
filterAudience (aud) {
if (!Array.isArray(aud)) {
aud = [ aud ]
}
return aud.some(a => OidcManager.domainMatches(this.providerUri, a))
}
The comments for method domainMatches state:
* Tests whether a given Web ID uri belongs to the issuer. They must be:
* - either from the same domain origin
* - or the webid is an immediate subdomain of the issuer domain
There's a mismatch between the form of the aud claim in the supplied access token and the form expected by OidcManager#domainMatches, i.e. clientId as opposed to webID. (We confirmed this in-house by adding extra debug output. The aud claim being tested by filterAudience() is a clientId, not a webID).
Fix?
According to RFC7519: JSON Web Token (JWT) - Section 4.1.3 "aud" (Audience) Claim
The "aud" (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected. In the general case, the "aud" value is an array of case-sensitive strings, each containing a StringOrURI value. In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.
OpenID Connect Core 1.0
In an ID token issued to a client, the aud claim must contain the client_id.
aud
REQUIRED. Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case sensitive strings. In the common special case when there is one audience, the aud value MAY be a single case sensitive string.
But, in the case of an access token, rather than an ID token, as far as I can see from the OIDC spec' the client doesn't use the aud claim at all when validating any access token it receives.
Given the definition of aud in the JWT spec' ('The "aud" (audience) claim identifies the recipients that the JWT is intended for.'), it seems to me that the access tokens issued by Solid need to be changed so that the aud field includes the iss claim or the sub claim, instead of (or in addition to) the client_id.
The
audclaim in OAuth tokens issued by Solid appears incorrect and is causing authentication errors when a client supplies the access token.Added 2019-Feb-01:
Added a TLDR re-iteration of the essential problem which triggered this issue, and the proposed fix, here for those not wanting to read the complete thread (though I recommend it, there's a lot of good information courtesy of @dmitrizagidulin)
@RubenVerborgh @kjetilk @dmitrizagidulin: Please could you comment?
/cc @kidehen @smalinin
Manifestation
OpenLink are seeing error
Token does not pass the audience allow filterwhen trying to bind a Solid pod to Virtuoso WebDAV using our WebDAV 'DET' functionality.Recreation
I've been able to replicate the problem, eliminating Virtuoso in the process.
I signed into a local instance of OSDB using solid.openlinksw.com:8444, then while logged in, I retrieved the access token from OSDB's oidc-rp storage.
Decoding the oauth token (a JWT) using https://jwt.io/
The "aud" (Audience) claim matches the clientId under key
rpConfig.session.clientIdin the oidc-rp data.Problem Source
(Line numbers refer to OpenLink's forks)
The error comes from:
AuthenticatedRequest#allowAudience@solid/oidc-rs/src/AuthenticatedRequest.js:354Function
audienceabove is a reference toOidcManager#filterAudience@solid/oidc-auth-manager/src/oidc-manager.js:251The filter function which is rejecting the
audclaim is defined at:@solid/oidc-auth-manager/src/oidc-manager.js:441The comments for method
domainMatchesstate:There's a mismatch between the form of the
audclaim in the supplied access token and the form expected byOidcManager#domainMatches, i.e.clientIdas opposed towebID. (We confirmed this in-house by adding extra debug output. Theaudclaim being tested byfilterAudience()is a clientId, not a webID).Fix?
According to RFC7519: JSON Web Token (JWT) - Section 4.1.3 "aud" (Audience) Claim
OpenID Connect Core 1.0
In an ID token issued to a client, the
audclaim must contain theclient_id.But, in the case of an access token, rather than an ID token, as far as I can see from the OIDC spec' the client doesn't use the
audclaim at all when validating any access token it receives.Given the definition of
audin the JWT spec' ('The "aud" (audience) claim identifies the recipients that the JWT is intended for.'), it seems to me that the access tokens issued by Solid need to be changed so that theaudfield includes theissclaim or thesubclaim, instead of (or in addition to) theclient_id.