Connect to a WebID with an OAuth2 authorisation code via the APIs

Context and running example

This how-to guide describes how you, as a developer, can use the Digita Gateway to make a continuous connection to a WebID of a user so you can read and write data to and from his or her pods.

In this guide, we will use an example to make the commands concrete.

Before you start

This guide assumes that you have access to an instance of Digita Gateway and that the user has a WebID dereferencing to a WebID profile document. This WebID profile document should contain at least one storage space (i.e. a pod) and at least one Solid-OIDC compliant OpenID Connect Issuer.

In the example,

Steps

Step 0: Authenticate as a client

Before you can get access to the API of the Digita Gateway, you should authorize yourself as an admin holder using a client credentials request. If your credentials are correct, you will get an access token which you have to pass along to get access to the API’s.

POST {{baseUrl}}/security/login/credentials HTTP/1.1
Content-Type: application/json

{
    "client_id": "URI of admin holder",
    "client_secret": "client secret"
}

In the example, the Digita Gateway has been configured with Lea as an admin holder with the following URI: http://localhost:3001/sparql/holder/lea. Lea has been given abc as a client secret.

This will result in the following API call:

POST {{baseUrl}}/security/login/credentials HTTP/1.1
Content-Type: application/json

{
    "client_id": "http://localhost:3001/sparql/holder/lea",
    "client_secret": "abc"
}

When executing this HTTP request, you should receive an access token. This token follows the JWT standard.

Now that you have an access token, you can make calls to the resources of the Digita Gateway through its HTTP REST API. To do so, you just add the token as an Authorization header with the Bearer scheme. In the remainder of this guide, we will store this token in to the {{token}} variable.

Step 1: Create a holder

Before you can connect a Solid pod (or better, a WebID) to the Digita Gateway, you first have to create a holder. A holder represents an entity which can have multiple identities (defined by identifiers) and external storage spaces.

In the Digita Gateway, a holder is identified by a URI. You can choose this URI yourself. You can then pass this URI using the following API request:

POST {{baseUrl}}/holder HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "uri": "URI for the holder"
}

In our example, we will create a holder tom and use the following URI for tom: http://localhost:3001/sparql/holder/tom

POST {{baseUrl}}/holder HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "uri": "http://localhost:3001/sparql/holder/tom"
}

Step 2: Create a purpose for the connection

In the Digita Gateway, a purpose contains the information about why certain types of data can be used. In this step we will create one.

You can provide the following attributes to the API call:
  • An icon

  • A description that describes the purpose

  • A list of predicates

  • A label

POST {{baseUrl}}/purpose HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "icon": "",
    "description": "Description Text",
    "predicates": [
        "http://www.w3.org/2006/vcard/ns#fn"
    ],
    "label": "Label Text"
}

In our example, we will create a purpose with the following attributes

POST {{baseUrl}}/purpose HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "icon": "",
    "description": "Description Text",
    "predicates": [
        "http://www.w3.org/2006/vcard/ns#fn"
    ],
    "label": "Label Text"
}

Step 3: Create an invite for the holder and purpose

Once you have created a holder and a purpose, you can create an invite so the holder can connect one or more external data sources such as those that are coupled to its WebID.

POST {{baseUrl}}/invite HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

 {
    "holder": "http://localhost:3001/sparql/holder/tom",
    "purpose": "{{purpose}}",
    "state": "not linked",
    "created": "1611566267000",
    "expires": "1640995200000"
  }

Solid-OIDC assumes the OAuth2.0 authorisation code flow with PKCE enabled. The Digita Gateway acts as an OAuth2.0 client and generates an OAuth2.O PKCE code_verifier and creates a code_challenge based on a S256-hash of the code_verifier.

The code_challenge, client_id and method parameters returned when you have posted the invite request and are required in the next step.

Step 4: Get OAuth2 authorisation code of the Subject’s OIDC issuer

As explained in the previous step, the Digita Gateway requires an OAuth2.0 authorisation code.

You can get such a code from a Solid-OIDC compliant identity provider.

To know which OIDC Issuer to use, you can use your browser to check the WebID of the user. In this WebID, you have to look for an solid:oidcIssuer entry. Then, you can user your browser to check the .well-known/openid-configuration-file of that OIDC issuer and look for authorization_endpoint. This is the endpoint that you need to query with the code_challenge to get the authorisation code and this is where the user will be asked to authenticate.

In the example, we’ve got https://pods.digita.ai/tom-haegemans/profile/card#me as a WebID. We see that the WebID dereferences a profile document that contains https://pods.digita.ai/ as a OIDC issuer.

When we browse to https://pods.digita.ai/.well-known/openid-configuration, we see that we can use the following URI as an OAuth2.0 authorisation end-point: https://pods.digita.ai/idp/auth.

Next, we will build a URI based on this knowledge and add the required OAuth and PKCE parameters. For our example, this results in the following URI (fill in your own parameters!):

https://pods.digita.ai/idp/auth?response_type=code&scope=openid&client_id=https%3A%2F%2Fpod.inrupt.com%2Fdigitatestpod%2Fpublic%2Fregistration.json&redirect_uri=http%3A%2F%2Flocalhost:3001%2Frequests.html&code_challenge=uTbIcV-CMgXhRJ0B5_OjQc7YV4g8rFgh3DIDxtMV3DI&code_challenge_method=S256

When browsing to this URI, the browser will prompt the WebID owner (i.e. the end user) to authorise the connection to the Digita Gateway. The end-user will then be redirected to the OAuth2.0 callback URI containing the Authorisation Code as a GET parameter.

In our example, we receive the following URI (after authorisation):

http://localhost:3001/requests.html?code=v-JQiu9Mv4XkavIhixo6P0_d6ZWsXl9qsvlPsfCu4O8

Step 5:

In the last step, we need to provide the OAuth2.0 authorisation code which we received from the GET parameter of the Callback URI and the callback URI itself to the following API call:app-name:

POST {{baseUrl}}/invite/encodedInviteURI/connection HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "code": "OAuth code",
    "redirect_uri": "OAuth callback URI"
}

In the example, we use the URI that identifies the invite we’ve created in the previous step and encode this URI using basic encoding. You can do this manually by using a tool such as https://www.urlencoder.org/.

This is how the API call looks like for our example.

POST {{baseUrl}}/invite/http%3A%2F%2Flocalhost%3A3001%2Fsparql%2Finvite%2Ff12381ba-e755-489e-988a-c5239027dd49/connection HTTP/1.1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "code": "-nr6C-ZR-oaYZZhg2YKmzH0gBRUlbgjyk2EdmREg1uH",
    "redirect_uri": "http://localhost:3001/requests.html"
}

Concluding discussion

That’s it! You’ve now manually used the Digita Gateway API and Solid interface to connect a Solid pod to the Digita Gateway as you would probably want to do in your own application.