Authenticate against your app's WebID using DPoP

Now that you have a use.id WebID for Apps, let's start by learning how to authenticate your app against this WebID. You will need to be authenticated against this WebID before you can access our API's (and also the pods your application's WebID has access to).

Authentication in Solid (and thus also use.id WebIDs) is a bit different than you are used to. This is because Solid OIDC requires you to use DPoP.

DPoP stands for Distributed Proof of Posession and is an addition to OAuth2. The aim of DPoP is to "prevent unauthorized or illegitimate parties from using leaked or stolen access tokens, by binding a token to a public key upon issuance and requiring that the client proves possession of the corresponding private key when using the token."

πŸ‘€

‍DPoP; tldr

DPoP headers add an extra layer of security because the resource server checks whether the public key of the DPoP proof used to get the access token is the same as the public key of the DPoP proof used to access the resource.

In what follows, we will show you how you can easily generate DPoP proofs and start using our API's.

If you want to have some detailed information on DPoP, make sure to check out this page.

πŸ€”

Why does use.id require DPoP proofs?

We know, DPoP makes it harder for you to use our API's. This is because many popular tools like e.g. Postman don't support DPoP out-of-the-box just yet. Be that as it may, DPoP is required by Solid and thus also to authenticate your app against its WebID. Because access to the use.id API is controlled based on WebID's, your app needs to authenticate using DPoP as well. Besides, it's good to have an additional layer of security, right?

Try it yourself: getting an access token

The best way to learn new things is to try it yourself.

Let's take this opportunity to learn how DPoP works by authenticating on the use.id Sandbox environment and getting an access token for your application.

Grab your favorite HTTP client and follow along.

Step 1: Generate a public/private key pair

The first step is to create a public/private key pair for your application. Check out the recipe below for instructions on how to do this in your favorite programming language.

An example of a key pair is shown below:

Public Key
{
     "kty": "RSA",
     "e": "AQAB",
     "use": "sig",
     "kid": "lJdU/aSiuFzOVBMcDQbpiTrcIvM=",
     "alg": "RS256",
     "n": "sa2Jman8Y_0miEuKMbpbG19m8Qgpj4lMlOR3TYorRHL2zVau_Ks3XC7XqxEcaqRXPyHfbJ-bZ7FZp_Vutboy7xJ7FTlwDkFga-YdjtpOfttdkEgZR6mzqun72l4Gb34enbG54UldUSJ4IDNQq2XoU0zKsKiDAiN417WdwN2oe1n1OK8wI9-1-j8hPW5rjdtiWxv0Y0UaUqZfpjUk2-D_0rkeXCCpXyBj_Q-Gbo5UqKi5N7ix64sUPUyR9vlfrd8gQWZPUNTvQaUrdCcMjxPtvoc3Tr83PGW2RxpTgiHO_QCylW6nS0maQbE0cUUNOBPljxFdcwowjIrTjaE2yhU89w"
}

Private Key
{
     "p": "-UgCR_m6oNoYQWhQgxMvWFJS0tvXVzHy7yXFex_mASkvtufuzzb_9sQMRpUyKF2jsj80E3MDLh9xW_0x47X5ezZsmsCan3AnFzkjHgB6yogx28RCrKOf7C8EfH_mixybZDJqqw2yLoKKv-XILoc7g3c4NGu_lSJC4uumzlYcoBE",
     "kty": "RSA",
     "q": "tnd6uf-Rxf4w8jMh6glypyiDmICaKX4c_9ObLjwCg0Abc5XgAuOZcTkvZbdng9M8HX9wf7uthgbeQKQHWBSmZRpXB0T_jOHgZQaaVP0bVDUZHYDPqzMUXPA78RKxyJS6PbfAbVOdttcVFztuhIlGVehyCqVTdFV-lZxRAfBDlIc",
     "d": "oWBAzfR6bEaNLNWPWsMr5Jyt3tXPCbNmfwVS5nZL_gt85duJa6wGyA8ziwgriyBgrdKSHxKID5IhIE_mkd_gSwu6Bvj4MbpnXRjQV-Mn0Ehlnpode91CdkW2ngswRSuDmy7E6T29J-fquzRgXIxeiuoAXptvAg49v44RDg9E62KtFvrgu4K4KNw5TRqXazb3-0KsxiqCLA3_oLoMwZpqXMOk75PdtTv0vdMpGBYivUkZ5QLFODoPdFGkcpHlBo14AZN_Wgiw7PmHHE-dT2u5DCIvJpKBBNg1kDB0iy-PcJaauTLPJuvJx82dtwySDca0kolSKyBTuql3ppjJp25RgQ",
     "e": "AQAB",
     "use": "sig",
     "kid": "lJdU/aSiuFzOVBMcDQbpiTrcIvM=",
     "qi": "Tf1fJAnFEVEOdbBmarqGMMzrmo8e1OtkWOH13Kgs-dY5goFlkTajgk1IYnMAvE2SoFfPmA3LBSdaMRqoF5vGYOVP_1u8Hok1QhNhUVxA-MOU3jEFvpRcjom1yjnnLrUdrGLCcp1WVKBXM931Si4-BYBFZq-I0wMjJLSHxYbF3iA",
     "dp": "3YDI1h_nX6qrxuLkN7RCa56rDcZe8JHnpezQL3nGrN1mEobAwsvCYgkOR2GnMgsSu_5BTc7y0ncKCY1QWWCHkJ5pnpDqVCYZ9h7FP5F5iqG-e-NGw5SZNAsLV31m49HFmiDae6gt3foNRDrvx7hOf6DeBmZ_1AqfmHeAvvPxd6E",
     "alg": "RS256",
     "dq": "OcnVYJsEfARpVAvFre2YFkDKjL4OFR4P6SYHe7kdwpvHFfBdpgyXSDioPjq72hBIdb8qzgmOh2gdc7N_b_H6UQLqlS7RELlo7BYPQuN2iFDYx3NIUhw3Usfy9ALLs48G_Qz5MKbOyF2OHHGCKw-e6w5XL-_0Yl_BW3jKXybNAHk",
     "n": "sa2Jman8Y_0miEuKMbpbG19m8Qgpj4lMlOR3TYorRHL2zVau_Ks3XC7XqxEcaqRXPyHfbJ-bZ7FZp_Vutboy7xJ7FTlwDkFga-YdjtpOfttdkEgZR6mzqun72l4Gb34enbG54UldUSJ4IDNQq2XoU0zKsKiDAiN417WdwN2oe1n1OK8wI9-1-j8hPW5rjdtiWxv0Y0UaUqZfpjUk2-D_0rkeXCCpXyBj_Q-Gbo5UqKi5N7ix64sUPUyR9vlfrd8gQWZPUNTvQaUrdCcMjxPtvoc3Tr83PGW2RxpTgiHO_QCylW6nS0maQbE0cUUNOBPljxFdcwowjIrTjaE2yhU89w"
}

Next, store the key pair somewhere secure because a resource server that requires DPoP will check whether the key pair remains the same for as long as you use a certain access token.

Step 2: Create a DPoP proof header for the authorisation endpoint

Now that you have a key pair, you can use it to create DPoP proof headers.

For this, you need to create a JWT with a header and payload that follows the structure as explained on this page, sign it using your private key and encode it.

We have created a tool to quickly generate DPoP proofs for experimental purposes.

You can also look at the recipe below to get an idea of how to create such a DPoP proof within your app.

To check whether your JWT is valid, you might want to use jwt.io.

Step 3: Get an access token using your DPoP proof

Now that you have created a DPoP proof for the authorisation endpoint, you can use this proof to get an access token using this endpoint.

Please refer to the recipe above for information on how to implement this step.

Step 4: Continue by creating a new DPoP proof for every new request

Voila, now you have an access token.

For each resource that you want to access using this token, you have to create a new and unique DPoP proof that is signed with the same private key that you used for the DPoP proof to get the access token.