Authentication and DPoP

In Solid, a person or app receives an identity token. This person or app should be able to use this token to retrieve a protected resource from any resource server (e.g. from storage.use.id and from pods.athumi.eu). However, when storage.use.id receives this token, it may not be able to impersonate (i.e. replay this token) to access data on pods.athumi.eu on behalf of the user. To prevent resource servers from replaying tokens, for each request, the user or app must bind the token to a specific resource server and endpoint and sign this combination with its private key.

This is what DPoP does.

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 prevent token replay and add other layers 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 Solid.

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

🤔

Where to use DPoP Proofs?

DPoP proofs add an extra layer of security and are especially relevant in a distributed setting such as Solid. However, DPoP proofs are a burden for developers because DPoP is not widely accepted leading to a lack of SDKs, ....

As such, in use.id, DPoP is only required when doing a request to a resource server. You do not have to provide DPoP proofs when using an internal use.id API.

Try it yourself: getting a Solid token and DPoP proof

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 identity 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 identity 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 identity 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 identity 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 identity 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 identity token.