Identity in Solid
Definitions
An application in Solid can be both a user-piloted app, like a mobile app, a traditional webapp with a user interface (either browser only or a browser app with a backend component) or a back-end service that acts autonomously with its own identity and has no user interface. Application is a broad term that just means software.
An agent in Solid is something that has autonomy. Both people and applications can be agents, as can organizations, devices or groups of people. We identify agents with a WebID.
An entity in Solid is a thing that exists. This can be a person, but it can also be a building or a car. Entities may or may not be agents. Entities have WebIDs.
A client in the broadest sense, is an application that utilizes the services of another. For instance, a backend application will be the client of an identity provider. A browser app will be the client of the Solid server and also a client of an identity provider. It is important to note that a client can mean different things in different contexts so the term is best avoided unless talking about specific contexts or flows (like the OAuth2.0 client credential grant for example).
Piloted Apps
A concrete example of this would be a browser-based web application. The user would be logged in with their own identity (using a WebID), while the web application (the client in this case) is distinctly identified in its own right (using its Client ID) and acts on behalf of the user in real-time. This is the most common interaction we see in Solid applications. In this case, the Solid server that the application is interacting with would see two identities: The user’s WebID and the application’s Client ID.
An autonomous software agent is an application that maintains its own identity and can act independently through pre-programmed logic. An example of this would be a back-end app accessing data in Pods to achieve some business function. Practically, this means that it has a WebID. It does not have a Client ID because it has a primary identity of its own, and there is no ‘medium’ through which it needs to act (like a user acting out their intentions through a browser app).
Unpiloted autonomous Apps
Unpiloted autonomous apps are applications that are written to act with the identity of a human user. For example, a script I might write to log into my photo app ‘as me’ and update photo metadata.
The app is autonomous but acts with the identity of the user as its primary identity.
One way in which authentication can be facilitated, is to assign the application a Client ID and secret (though an OAuth 2.0 client credentials grant). The Client ID is reflected in the ID tokens it receives alongside the primary WebID. If used in production, these credentials must be associated with a service account in the OIDC Provider.
WebIDs and Client IDs: What's the difference?
WebID
A WebID is a URI that identifies an entity. It resolves to a WebID profile document, which contains information to aid other Solid applications in working with it (such as the entity’s trusted identity providers or Solid Pods). It is carried in Solid-OIDC ID Tokens in the ‘webid’ and ‘sub’ claims. WebID is the first-class identifier in Solid today and can be used to identify people, organizations or things.
Client ID
A Client ID is an OAuth 2.0 concept that describes a public identifier string for a client application. In OAuth 2.0 flows, the Client ID has typically been a UUID or other pseudorandom string. In statically registered applications, the Client ID forms one-half of a client credential set (along with the client secret) used to authenticate applications. It is defined by the identity provider in these static registration flows.
In Solid-OIDC flows, the Client ID is determined by the application developer and is the URL of the public Client ID Document. This document contains important information to aid discovery and guarantees client authenticity by virtue of DNS ownership (which negates the need for client credentials generated by the identity provider).
User Experience: Who’s asking?
Client IDs and WebIDs are among several identifiers used by Solid servers to filter which agents and client applications can access them. When both of these identifiers are dereferenced, they also provide information to aid the user experience, such as displaying information about the agent, client, or organization that wants to act on data in a Pod.
Client ID
In traditional OAuth 2.0 flows, a Client ID would be a statically registered UUID provided by the IDP. This type of Client ID was never designed to be dereferenceable or to provide any other function than that of a unique identifier.
For Solid applications, a Client ID should be a URL that resolves to a JSON-LD (as a minimum, though additional, content-negotiated variants are permissible) Client ID Document. Whilst the primary purpose of the Client ID Document is to convey metadata to facilitate the OIDC flow, the Client ID Document can also provide information to allow the user to to make an informed decision about whether to proceed with allowing the application to access the users ID Token. This is typically in the form of a company logo or a description of the application.
Developers should make the information in Client ID Documents as descriptive as possible. It should be noted that the Solid OIDC Client ID Document is an OIDC Dynamic Client Registration Client Metadata set and must follow the rules laid out in the specification. The purpose of this document is to provide the same metadata that would be provided in the traditional OAuth static registration flow.
Depending on the access control policies on the Solid Server, once a user accepts, the application may or may not be able to act on data in the Pod using the user’s ID token.
WebID
In almost all enterprise Solid data sharing flows, an organization will ask for permission to access a user’s data via some back end autonomous agent. In order for the user to know who is asking for permission, the access management application needs to be able to display information from an organization's WebID profile so that the user can make an informed decision. To provide the best user experience, an organization or logical business unit of an organization will want to approach a user with a consistent identity, no matter which backend service is requesting the data.
There are no hard and fast rules about when to use a corporate WebID vs one for individual applications. The guiding principle should be to get the most transparent and understandable user experience. For example, if the customer of a bank expects the mortgage department to be a different entity than the one that deals with business bank accounts, then the services that act on behalf of these entities should identify themselves separately. Alternatively, if the mortgage department has multiple services that act on its behalf (such as deal calculation, payments and customer contact), then they should all probably act with the same identity.
Trust in WebIDs
The WebID embodies two facets of trust, that of the URI itself and of the Identity Provider (the ‘Issuer’) associated with it. The two are linked by the WebID profile, via the solid:oidcIssuer predicate. Providing the WebID provided in the ID token is correct AND the OIDC issuer of that token is exactly referenced in the WebID profile, the identity can be assumed to be valid.
Examples
User Piloted Application

Here, the user pilots an application running in a web browser. The browser application has a Client ID Document at ‘https://example.com/client_id‘ which is determined and hosted by the application developer. This Client ID (https://example.com/client_id) will be dynamically registered on first contact with the IDP, although the Solid server must be configured to allow this client to act on the Pod. The WebID used to identify the agent interacting with the Solid Server is the user’s WebID. Before the Identity Provider allows the user to proceed with the login, it will use the information from the Client ID Document and display this information to the user to make sure they are happy to proceed.
The Client ID Document could look like this:
{
"@context":[
"https://www.w3.org/ns/solid/oidc-context.jsonld"
],
"client_id":"https://example.com/client_id",
"client_name":"My Example app",
"client_uri":"https://example.com/",
"redirect_uris":[
"https://example.com/login"
],
"grant_types":[
"authorization_code",
"refresh_token"
],
"scope":"openid webid offline_access",
"token_endpoint_auth_method":"none",
"logo_uri":"https://example.com/logo.png",
"tos_uri":"https://example.com/terms.html",
"policy_uri":"https://example.com/policy.html",
"contacts":[
"[email protected]"
],
"application_type":"web",
"require_auth_time":false
}
The user’s WebID could look something like: https://id.inrupt.com/adam
It will resolve to a WebID profile document that could look something like this:
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix solid: <http://www.w3.org/ns/solid/terms#> .
[ a foaf:PersonalProfileDocument ;
foaf:primaryTopic <https://id.inrupt.com/adam>
] .
<https://id.inrupt.com/adam>
a foaf:Agent ;
rdfs:seeAlso <https://storage.inrupt.com/5ce2a23e-a19c-47f6-a8c3-e1baa7529494/extendedProfile> ;
<http://www.w3.org/ns/pim/space#storage>
<https://storage.inrupt.com/5ce2a23e-a19c-47f6-a8c3-e1baa7529494/> ;
solid:oidcIssuer <https://login.inrupt.com>;
foaf:isPrimaryTopicOf <https://storage.inrupt.com/5ce2a23e-a19c-47f6-a8c3-e1baa7529494/extendedProfile> .
Thus - the ID token generated by the IDP and presented to the Solid Server will look like:
{
"sub": "https://id.inrupt.com/adam",
"aud": [
"solid",
"https://example.com/clientid"
],
"azp": "https://example/clientid",
"webid": "https://id.inrupt.com/adam",
"iss": "https://login.inrupt.com",
"jti": "844a095c-9cdb-47e5-9510-1dba987c0a5f",
"iat": 1603370123,
"exp": 1603371007,
"cnf": {
"jkt": "8876fg6shTg-jsUsjKjshh873_sT65GtFfghsf7"
}
}
Note the Client ID of the app in the aud and azp claims, and the users WebID in the sub and webid claims.
If a backend for frontend pattern is used, the Client ID, WebID and ID token would be the same, except that the backend for frontend holds the ID and Access tokens, and the browser app just maintains a session cookie.
Autonomous Software Agent with IDP
When a backend service wants to interact with Pods either via an access request or using ACP, the preferred pattern is that it obtains an identity token containing its WebID via a trusted OIDC identity provider. How the autonomous entity authenticates to get its identity token is up to the identity provider eg - via certificates or the OIDC client_secret_jwt, private_key_jwt methods.
This arrangement allows the identity provider to create identity tokens with the right WebID for the given context, which allows for a more flexible application and organizational identity proposition.
For example, multiple back end services could be identified individually when interacting within the organization, but act with a unified organizational identity when interacting outside the organization. This would be managed by the organization's administrators via their identity provider. This would give a better experience for users, who would be presented with access requests from a single organization WebID, no matter what particular backend service needed the data.

The application’s WebID is the only identifier presented to the Solid Server. It could look something like this https://webid.example.com , with a WebID profile looking like:
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
@prefix skos: <http://www.w3.org/2004/02/skos/core#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
<https://webid.example.com>
a foaf:Organization, foaf:Agent;
skos:prefLabel "Example Corporation";
foaf:logo <https://example.com/logo.png>;
foaf:homepage <https://example.com>;
solid:oidcIssuer <https://login.example.com/> .
This would result in an OIDC token looking like:
{
"sub": "https://webid.example.com",
"aud": [
"solid"
],
"webid": "https://webid.example.com",
"iss": "https://login.example.com",
"jti": "844a095c-9cdb-47e5-9510-1dba987c0a5f",
"iat": 1603370123,
"exp": 1603371007,
"cnf": {
"jkt": "8876fg6shTg-jsUsjKjshh873_sT65GtFfghsf7"
}
}
Self Asserted autonomous agent
A similar pattern to Autonomous Agent with IDP, except in this case the back end application asserts its own identity and acts as its own identity provider.

This can be effective if there is no advanced identity provider in the system, or where there is a small number of backend agents. With this pattern, it is also possible to have numerous backend agents share private keys so that they can all act with the same identity if required or act with the same issuer (although managing this can become difficult as the system scales).
The application’s WebID is the only identifier presented to the Solid Server. It could look something like this https://webid.example.com , with a WebID profile looking like:
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
@prefix skos: <http://www.w3.org/2004/02/skos/core#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
<https://webid.app1.example.com/>
a foaf:Organization, foaf:Agent;
skos:prefLabel "Example Corporation - App 1";
foaf:logo <https://example.com/logo.png>;
foaf:homepage <https://example.com>;
solid:oidcIssuer <https://app1.example.com/idp> .
This would result in an OIDC token looking like:
{
"sub": "https://webid.app1.example.com",
"aud": [
"solid"
],
"webid": "https://webid.app1.example.com",
"iss": "https://login.example.com",
"jti": "844a095c-9cdb-47e5-9510-1dba987c0a5f",
"iat": 1603370123,
"exp": 1603371007,
"cnf": {
"jkt": "8876fg6shTg-jsUsjKjshh873_sT65GtFfghsf7"
}
}
Unpiloted Autonomous Apps
In this case, a user registers their application ahead of time and provides the client credentials created by the IDP to their application.
The WebID given to the Solid Server will be that of the User, and the unpiloted app will present a Client ID provided by the IDP. In this case, the Client ID may not be dereferenceable as it will have been generated directly by the identity provider and thus might not have any facility to host documents.
In this pattern, the app acts on behalf of the user in the IDP, which could be a human user or a service account.

WebID: https://id.inrupt.com/adam
WebID Profile
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix solid: <http://www.w3.org/ns/solid/terms#> .
[ a foaf:PersonalProfileDocument ;
foaf:primaryTopic <https://id.inrupt.com/adam>
] .
<https://id.inrupt.com/adam>
a foaf:Agent ;
rdfs:seeAlso <https://storage.inrupt.com/5ce2a23e-a19c-47f6-a8c3-e1baa7529494/extendedProfile> ;
<http://www.w3.org/ns/pim/space#storage>
<https://storage.inrupt.com/5ce2a23e-a19c-47f6-a8c3-e1baa7529494/> ;
solid:oidcIssuer <https://login.inrupt.com> ;
foaf:isPrimaryTopicOf <https://storage.inrupt.com/5ce2a23e-a19c-47f6-a8c3-e1baa7529494/extendedProfile> .
ID Token
{
"sub": "https://id.inrupt.com/adam",
"aud": [
"solid",
"D8702B90-7367-407A-AD75-0951D58631C3"
],
"azp": "D8702B90-7367-407A-AD75-0951D58631C3",
"webid": "https://id.inrupt.com/adam",
"iss": "https://login.inrupt.com",
"jti": "844a095c-9cdb-47e5-9510-1dba987c0a5f",
"iat": 1603370123,
"exp": 1603371007,
"cnf": {
"jkt": "8876fg6shTg-jsUsjKjshh873_sT65GtFfghsf7"
}
}
IDP Anti Patterns
It is of course possible to generate ID tokens with a Client ID pertaining to a specific instance of software and a WebID of the hosting / authoring organization. We would call this an anti-pattern and not recommend this.
In Solid, Client IDs should be dereference to Client ID Documents. These documents are primarily for the purpose of facilitating OIDC flows with the necessary metadata. Where possible, entities in Solid ecosystems should identify themselves with a primary identity in the form of a WebID.
Last updated