Current Spec version: v.0.1.0
(see CHANGELOG.md)
WebID-OIDC is an authentication delegation protocol (as well as a toolkit of useful auth-related verification techniques) suitable for WebID-based decentralized systems such as Solid, as well as most LDP-based systems. It is based on decentralized OAuth2/OpenID Connect.
- Introduction
- Differences from Classic OpenID Connect
- Brief Workflow Summary
- Deriving WebID URI from ID Token
- WebID Provider Confirmation
- Authorized OIDC Issuer Discovery
- Detailed Sign In Workflow Example
- Workflow Example for Tokens Representing the App Itself
- Workflow Example for Tokens Representing a User via App Itesef
- Decentralized Authentication Glossary
The end result of any WebID-based authentication workflow is a verified WebID URI (specifically, the recipient verifies that the agent controls that URI). For example, WebID-TLS derives the WebID URI from a TLS certificate, and verifies the certificate against the public key in an agent's WebID Profile. Similarly, the end result of OpenID Connect (OIDC) workflows is a verified ID Token. The WebID-OIDC protocol specifies a mechanism for getting a WebID URI from an OIDC ID Token, and gains the benefits of both the decentralized flexibility of WebID, and the field-proven security of OpenID Connect.
See also: Motivation for WebID-OIDC.
- Fully decentralized cross-domain authentication (any peer node can serve as an identity provider as well as a relying party to any other node) made possible by Proof of Posession (PoP) Tokens.
- Builds on decades of real-world authentication industry experience
- Incorporates lessons from, and fixes to threat models of: SAML, OpenID and OpenID 2, OAuth and OAuth 2. See, for example, RFC 6819 - OAuth 2.0 Threat Model and Security Considerations -- OpenID Connect was developed in large part to address the threats outlined there.
- Stands on the shoulders of giants (makes use of the JOSE suite of standards for token representation, cryptographic signing and encryption, including JWT, JWA, JWE and JWS)
- Sign Off (and Single Sign Off) capability
- Capability for revocations, black lists and white lists of both providers and client apps
- Supports authentication for the full range of agents and clients: in-browser Javascript apps, traditional server-side web apps, mobile and desktop apps, and IoT devices.
- Compatibility with existing Web Access Control ACL implementations such as those in Solid servers.
- Sets up the infrastructure for adding Capabilities functionality to Solid
If you're not familiar with the OIDC/OAuth2 workflow, you should do the following:
- Read the Brief Workflow Summary section below
- Refer to the Decentralized Authentication Glossary to help clarify how the various terms (Relying Party, Provider, etc) apply to WebID systems.
- Read the OpenID Connect explained article. Becoming familiar with the basic OIDC concepts will be quite helpful with understanding this spec.
WebID-OIDC makes the following changes to the base OpenID Connect protocol (which itself improves and builds on OAuth 2):
- Discusses and formalizes the Provider Selection step.
- Adds a procedure for Deriving a WebID URI from ID
Token, since WebID-based protocols use
the WebID URI as a globally unique identifier (rather than the combination of
iss
uer andsub
ject claims). - Adds an additional step: WebID Provider Confirmation. After the WebID URI is extracted, the recipient of the ID Token must confirm that the Provider was indeed authorized by the holder of the WebID profile.
- Specifies the Authorized OIDC Issuer Discovery process (used as part of Provider Confirmation, and during Provider Selection steps).
- Utilizes PoP tokens as a means to access a wide array of resource providers.
It's also worth mentioning that while traditional OpenID Connect use cases are concerned with retrieving user-related claims from UserInfo endpoints, most WebID based systems replace the UserInfo mechanism with the contents of WebID Profile documents.
The overall sign in workflow used by the WebID-OIDC protocol is as follows.
For example, here is what happens when Alice tries to request the resource
https://bob.example/resource1
.
- Initial Request: Alice
(unauthenticated) makes a request to
bob.example
, receives an HTTP401 Unauthorized
response, and is presented with a 'Sign In With...' screen. - Provider Selection: She selects
her WebID service provider by clicking on a logo, typing in a URI (for
example,
alice.solidtest.space
), or entering her email. - Local
Authentication:
Alice gets redirected towards her service provider's own Sign In page, thus requesting
https://alice.solidtest.space/signin
, and authenticates using her preferred method (password, WebID-TLS certificate, FIDO 2 / WebAuthn device, etc). - User Consent: (Optional) She'd also be
presented with a user consent screen, along the lines of "Do you wish to
sign in to
bob.example
?". - Authentication Response:
She then gets redirected back towards
https://bob.example/resource1
(the resource she was originally trying to request). The server,bob.example
, also receives a signed ID Token fromalice.solidtest.space
that was returned with the response in point 3, attesting that she has signed in. - Deriving a WebID URI:
bob.example
(the server controlling the resource) validates the ID Token, and extracts Alice's WebID URI from inside it. She is now signed in tobob.example
as userhttps://alice.solidtest.space/#i
. - WebID Provider Confirmation:
bob.example
confirms thatsolidtest.space
is indeed Alice's authorized OIDC provider (by matching the provider URI from theiss
claim with Alice's WebID).
There is a lot of heavy lifting happening under the hood, performed by bob.example
and alice.solidtest.space
, the two servers involved in this exchange. They
establish a trust relationship with each other (via
Discovery, and Dynamic
Registration),
they verify each other's signatures against their public keys, and verify
Alice's client app (if she's using one). Fortunately, all of that complexity is
hidden from the user (and most of it is also hidden from the app developer).
A WebID-OIDC conforming Relying Party tries the following methods, in order, to obtain a WebID URI from an ID Token:
First, check the ID Token payload for the webid
claim. This claim is added to
the set of OpenID Connect ID
Token claims by
this WebID-OIDC spec. (Note that the set of ID Token claims is extensible, by
design, as explained in the OIDC Core spec.) If the webid
claim is present in
the ID Token, its value should be used as the WebID URI by the Relying Party,
instead of the traditional sub
(subject identifier) claim.
This method is useful when the identity providers issuing the tokens can
customize the contents of their ID tokens (and can add the webid
claim).
If the webid
claim is not present in the ID Token, the Relying Party should
check if the sub
claim contains as its value a valid HTTP(S) URI. If the value
of the sub
ject claim is a valid URI, it should be used as the WebID URI by the
Relying Party.
This method is useful when the identity providers issuing the tokens cannot add
claims but can set their own values of their sub
ject claims (that is, they're
not automatically generated as UUIDs, etc).
If a WebID URI is not found in either the webid
or sub
claim, the Relying
Party should proceed to make an OpenID Connect UserInfo
Request, with
the appropriate Access Token that it received alongside the ID Token. This
method is provided for cases where users do not have control over the contents
of the ID Tokens issued by their Providers and so would not be able to use the
previous methods. This would be the case, for example, if a user wanted to sign
in to a WebID-OIDC Relying Party using an existing mainstream Provider such as
Google. Once the UserInfo response is received by the Relying Party, the
standard website
claim should be used as the WebID URI by that RP.
The OIDC spec uses the ID Token's sub
ject claim as a unique user id. However,
it requires that the id is unique for a given Provider. Given that a WebID
is a globally unique user identifier, the WebID-OIDC protocol needs to take
an additional step and confirm that the holder of that WebID has authorized
a given Provider to use that WebID. Otherwise, the following situation can
happen:
- Alice logs in to
bob.example
with her identity Provider of choice,alice.example
. The ID Token fromalice.example
claims Alice's WebID ishttps://alice.example/#i
. So far so good. - An attacker also logs in to
bob.example
, usingevilbox.com
as an identity Provider. And because they happen to control that server, they can put anything they want in thewebid
claim of any ID Token coming out of that server. So they also claim that theirwebid
ishttps://alice.example/#i
.
Without an additional confirmation step, how can a recipient of an ID Token
(here, bob.example
) know which of those login attempts is correct? To put it
another way, how can a recipient know which Provider is approved by the
owner of the WebID?
When presented with WebID-OIDC credentials in the form of bearer tokens,
the Resource Server MUST confirm that the Identity Provider (the value in the
iss
uer claim) is authorized by the holder of the WebID, by doing the
following:
- (Common case) If the server that issued the ID Token is the same entity that
hosts the WebID profile, then it is considered the authorized OIDC provider
for that WebID (short-circuiting the Provider Confirmation process), and no
further steps need to be taken. Specifically, one of the following must be
true:
- The origin
of the WebID URI is the same as the origin of the URI in the
iss
uer claim. (For example,iss: 'https://example.com'
and the WebID URI ishttps://example.com/profile#me
). - The WebID URI is a subdomain of the issuer of the ID Token.
For example,
iss: 'https://example.com'
and the WebID URI ishttps://alice.example.com/profile#me
. If neither of the above is the case (and the WebID is hosted on a security realm different than that of its OIDC provider), further steps need to be taken for confirmation.
- The origin
of the WebID URI is the same as the origin of the URI in the
- Determine the authorized OIDC provider URI for that WebID, by performing Authorized OIDC Issuer Discovery.
- If the Provider URI is not discoverable (either from the header or the body of the WebID Profile, the Resource Server MUST reject the credentials or authentication attempt.
- If the Provider URI is discovered, it MUST match the Issuer URI in the ID
Token (the
iss
claim), reject the credentials otherwise.
During the Provider Selection or Provider Confirmation steps it is necessary to discover, for a given WebID, the URI of the authorized OIDC provider for that WebID.
- First, attempt to discover from link headers
- If not found, proceed to discover from the WebID Profile
Note that this procedure is different from the classic OpenID Provider Issuer Discovery process, since that spec is concerned with discovering the issuer URI from a user's email address, using the WebFinger protocol. Whereas this spec needs to derive the issuer URI from a WebID URI (which is often hosted on a different domain than the issuer).
Note: this feature is at risk of being removed. Please join the discussion. Code depending on this will still work for now.
To discover the authorized OIDC Issuer for a given WebID from Link rel headers:
- Make an HTTP OPTION request to the WebID URI.
- Parse the
Link:
header, and check for the value of thehttp://openid.net/specs/connect/1.0/issuer
link relation. If present, use the value of that link relation as the authorized Provider URI. For example:Link: <https://provider.example.com>; rel="http://openid.net/specs/connect/1.0/issuer"
means thathttps://provider.example.com
is the authorized OIDC provider for that URI. - If the Link header is not present (or does not contain the relevant link relation), proceed to discovering the issuer from the WebID profile contents.
To discover the authorized OIDC Issuer for a given WebID from the WebID Profile contents (this requires Turtle/RDF parsing capability):
- Dereference the WebID URI (make an HTTP GET request) and fetch the contents of the WebID Profile (typically in Turtle or JSON-LD format or some other RDF serialization).
- Parse the RDF, and query for the object of the statement containing the
<http://www.w3.org/ns/solid/terms#oidcIssuer>
predicate.
For example, if Alice (with the WebID of https://alice.example.com/profile#me
)
wanted to specify https://provider.com
as the authorized OIDC provider for
that profile, she would add the following triple to her profile:
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
# ...
<#me> solid:oidcIssuer <https://provider.com> .
WebID-OIDC must deal with a number of RSs many of which the OP will not know about. OIDC defines the aud
claim which defines the RSs for which a token can be used.
However, given Solid's use case, a token should be usable for any RS so the user may federate a query across multiple Pods, so the aud
ience cannot be constrained. Yet, an unconstrained aud
ience opens up the possibility of token stealing. In this case, a user sends a request to evilPod.example
. The Pod returns the requested information, but now has the user's token and may pretend to be the user on any other Pod in the world.
The solution employs Proof of Possession (PoP) tokens changing the way the Bearer token is constructed:
- A client application generates a short-lived public and private key.
- The client generates a request
JWT
just as it would under normal OIDC with the addition of akey
field containing the public key. - Authentication proceeds normally and yields a signed
id_token
where theaud
ience is the client application (represented by theorigin
of the providedredirect_uri
) and an additional fieldcnf
is provided containing the client's public key. - Before sending requests to any RSs, the client generates a new signed JWT PoP token containing the RS's uri as the
aud
ience and anid_token
feild containing theid_token
provided by the OP. - When an RS receives the PoP token, it MUST reject any tokens containing a mismatched audience or a signature that is not associated with the public key in the
cnf
claim.
To walk through a more detailed example for WebID-OIDC login, refer to the Example WebID-OIDC Workflow doc.
For a detailed example of how an application/agent can access resources in a pod on behalf of a given user, refer to the Example Application OIDC Workflow.
For a detailed example of how an application/agent can gain a token representing the user via the application, refer to the Example Application User OIDC Workflow.
In order to discuss decentralized authentication protocol details, it would be helpful to familiarize ourselves with the terminology that is frequently used by various decentralized protocol specs (such as OAuth2, OpenID Connect).
Human user. If the user is an app or service (that has its own WebID Profile),
this can be generalized to Agent
. Also called Resource Owner
. In the
following examples, Alice and Bob are Users.
A formal name for a Browser
. Note that this is often separate from a Client
application (in many cases, client apps written in Javascript run inside the
browser).
An OpenID Connect Identity Provider
(called OP
in most OIDC specs). Also
sometimes referred to as Issuer
. This can be either a POD (see below) or an
external OIDC provider such as
Google. In
the spec, Alice's POD, alice.example
, will mostly play the role of a Provider.
A server hosting resources that the user wants to access, such as HTML, images,
Linked Data / RDF sources, and so on. In the spec, bob.example
will be used as
the Resource Server
(that is, Alice will be requesting resources on Bob's
server). Note: In the traditional federated social sign on context, a
provider (such as Facebook) serves as both an Identity Provider and a
Resource Server.
A Relying Party
is a POD or a client app that has to rely on an ID Token
that's issued by a Provider. In the spec, when Alice tries to access a resource
on bob.example
, Bob's POD acts as the Relying Party, in that interaction.
And correspondingly, Alice's POD, alice.example
, will serve as the Identity
Provider, again for that interaction.
Incidentally, when Alice tries to access a resource on her own POD,
alice.example
plays all of the roles -- it's both the Provider and a Relying
Party (as well as the Resource Server).
A Personal Online Datastore (POD for short). It plays several roles -- firstly,
it stores a user's data (and so acts as a Resource Server
). In many cases, it
also hosts the user's WebID Profile, and implements the API endpoints that allow
it to act as a WebID-OIDC Identity Provider (OP). Lastly, when users requests
resources from it, the POD also acts as a Relying Party (a recipient of those
users' ID Tokens).
In this spec, alice.example
and bob.example
are both PODs.
A user's Home POD is one that hosts their WebID Profile, and also acts as that user's Identity Provider. We use the term Other POD in this spec to denote some other WebID-OIDC compliant POD, acting as a Resource Server and Relying Party, that a user is trying to access using the WebID URI and Profile of their Home POD.
When Alice tries to access a resource on Bob's POD, alice.example
is her Home
POD, and bob.example
plays the role of the Other POD.
Public - in-browser, mobile or desktop app, cannot be trusted with securely storing secrets (private key material, secret client IDs, etc). Confidential - server-side app, can be trusted with secrets.
A public client app that is trying to present a user's credentials from their
home POD to some other POD. For example, Bob is trying to access, via a client
app, a shared file on Alice's alice.example
POD, logging in using his own
bob.example
POD/provider. In this example, bob.example
is a Provider, alice.example
is
a Relying Party, and the client app (say, a browser-based HTML editor) is a
Presenter.