Context: I’m building an iOS native mobile (Swift) authentication library for Solid pods. The design is intended to have the initial part of the authentication (up to and including the browser redirect) work only on the iOS client and later steps (e.g., refresh token request) work either on iOS or on backend Swift-based servers (e.g., running on Linux).
Some Solid pod issuers don’t use DPoP’s for /token requests (e.g., https://broker.pod.inrupt.com/). They use client_secret_basic or client_secret_post.
My first question is: How do you tell from the discovery document for a given Solid pod issuer whether it accepts DPoP’s for /token requests? e.g., what should I see in the contents of token_endpoint_auth_methods_supported?
My second question: Do any current Solid pod implementations currently allow use of DPoP’s for /token requests?
I had though that I had been using DPoP’s to successfully do /token requests with some issuers (e.g., https://solidcommunity.net), but it seems that the DPoP header wasn’t needed. I just ran a test and with none of client_secret_basic, client_secret_post, or DPoP it generated tokens.
There may be a confusion here, because DPoP tokens are not equivalent to client_secret_basic or client_secret_post, and one cannot be used instead of the other. Rather, they are complementary mechanisms achieving different goals:
client_secret_basic or client_secret_post are ways for the client to authenticate to the OIDC issuer’s token endpoint. They are only meaningful to the issuer, and they are meant to be shared with it by the client.
DPoP is a way of “reducing” the scope of an access token in order to prevent replay attacks. A client gets a DPoP-bound access token by generating a key pair, and sending the public key along with its token endpoint request. If the OIDC issuer supports DPoP, it returns a DPoP-bound token, which means the token is associated to the public key provided by the client. Then, in order to send an authenticated request to the Resource Server, the client sends the DPoP-bound token in the Authorization header, and it also includes an additional header where it signs proof that shows it has the private key paired with the public key associated to the access token. The proof is specific to the method used (GET, POST…) and to the target URL. This way, a malicious resource server isn’t able to replay the access token to another resource server in order to access private resources it shouldn’t have access to, because the access token is worthless without the associated proof, and the proof can only be generated by the client which controls the private key.
With this in mind, unfortunately, there is no way (that I know of) to know a priori if a given OIDC issuer supports issuing DPoP tokens, only looking at its /.well-known/openid-configuration document. That’s actually not entierly true: the Solid-OIDC specification mandates that Access Tokens are DPoP-bound, and it requires that conforming Solid Identity Providers add the following entry to their configuration discovery document: "solid_oidc_supported": "https://solidproject.org/TR/solid-oidc" (see the specification). However, the specification still being a draft, not all Solid Identity Providers support this discovery method.
To your second question, I think most if not all Solid Identity Providers and Pod Servers support issuing and verifying DPoP-bound access tokens. Solid being an open ecosystem, such mechanism is an important security feature. To be specific, they are supported by ESS (so https://broker.pod.inrupt.com/ issues them, and https://pod.inrupt.com/ verifies them), NSS (so https://solidcommunity.net both issues and verifies them), and CSS (there aren’t any public instances of the Community Server yet).
Have to say I’m even more confused. In section 14. Token request with code and code verifier the client uses a /token request with a DPoP header. This is not an access token. There is no client_secret_basic or client_secret_post present for authentication. The client is not getting a DPoP-bound access token in this case. It is using a DPoP header to make a /token request.
Hello! There are 2 different uses of DPoP proofs: the first one is on the /token endpoint of the identity provider, to get an access token, and the second one is on the resource server along with the access token, to prove that you are the legitimate user of the access token.
The AJAX request that you link to in the Solid primer is for the first case, so the client does not present an access token. The identity provider will answer something in the line of { “id_token”: “eyJ…”, “access_token”: “eyJ…”, “token_type”: “DPoP”, “refresh_token”: “…” }. The access token is signed by the identity provider, and once decoded, contains a field “cnf”: {“jkt”: “…” } where the JKT is the hash of the client key. So, when later checking the access token on the resource server, you must prove that you own the key identified by jkt, by issuing the second kind of DPoP proof. The fact that using the access token requires the client to prove that it owns a key that hashes to jkt is what we mean by using a “DPoP-bound access token”.
Ah right, I get the confusion: in the primer, the client authentication method is neither client_secret_basic nor client_secret_post (which both are “regular” OIDC client authentication methods), it is using a method specific to Solid-OIDC by providing a client ID which is an IRI as part of the request body. It’s a special kind of client_secret_post (sort of), where the client information aren’t registered to the OIDC provider, either statically out-of-band or dynamically at runtime, but they are available at an IRI which is a kind of WebID for the Client, introduced in the Solid-OIDC spec as Client Identifiers.
The presence of the DPoP header serves a different purpose: as I mentioned, it’s not about identifying the Client, but it indicates to the OIDC provider that the client is in control of the private key bound to the public key presented in the header, which means the OIDC provider may issue in its response an Access Token which should be bound to the provided key (as described in the following steps of the primer. Note that the client_id provided as part of the token request body is also present in the Access Token.
It seems like there should be a reference in 14. Token request with code and code verifier to the other methods to authenticate with a /token request. E.g., so other folks like me can avoid this rabbit hole. Not sure where to file an issue for this.
@zwifi has already described much of this, but I’ll try to restate it so it is more clear.
DPoP has no relationship to client authentication. DPoP only binds a keypair to a particular token. Also, DPoP is opt-in for apps (like with PKCE). As such, it is highly recommended but not required in all circumstances. If you are unsure whether to use DPoP in your authentication flows, then please use DPoP.
Client authentication can take several forms. Client authentication is only relevant for confidential OAuth2 clients (i.e. statically or dynamically registered OAuth2 clients). client_secret_basic and client_secret_post are two examples, but there are others. Determining which mechanisms a server supports can be found by looking at the /.well-known/openid-configuration resource for the OIDC issuer.
When a Solid app uses a Solid OIDC Client Identifier, there is no authentication at the token endpoint. This is by design: here, the app is using a public OAuth2 client, and there is no authentication (by definition) for public clients.
Finally, the Solid-OIDC primer most certainly needs to be updated to provide greater clarity. It currently lags behind the specification in several ways.
I think most if not all Solid Identity Providers and Pod Servers support issuing and verifying DPoP-bound access tokens. Solid being an open ecosystem, such mechanism is an important security feature.