Skip to content

'aud' claim in Solid OAuth tokens #1061

@cblakeley

Description

@cblakeley

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageIssues that need team review

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions