This question: Server to Server interactions was asked a couple of years back, but I’d like to elaborate it a bit – so making a new topic/question.
My context is: I have a client mobile app and a (non Solid) server. I want to add use of Solid Pods in so that someone can sign in to a Solid pod from the client, and allow the server to make requests of the Solid pod. This is a fairly common client-server usage pattern in industry currently. For example, my client and server already do this with both Google Drive and Dropbox. For example, a user can sign in on the client with Google, and my server can make requests on their behalf to their Google Drive.
My main question is related to DPoP usage. I’ve been reading Solid OIDC Primer and from that document-- “A new DPoP token must be generated every time a request is made.” How should I go about doing this for my client/server use case? Does the public/private key used to generate the DPoP token have to also be sent to the server? (Which seems like a security risk).
Your use case seems like a perfectly normal thing to do. The client (your web application) stores a secret key to sign the DPoP proofs for all users. You should keep this key on the web application server, not in the web browser. If you can’t store things privately in the web application, then you need to generate a key for each user.
When the user logs in to his pod, you get an authorization code that you trade for an access token, like what you do for google drive and dropbox. The difference is that you also give the token endpoint your public key so that the identity provider can sign it in the access token. You do that by simply signing the token request with, again, a DPoP proof. When you get an access token, it contains a cnf/jwk field that identifies your key.
Thanks for your response. Just to be clear on terminology, I’m going to define things this way:
a) A Solid Server: A server somewhere else supporting Solid
b) Client: My mobile (iOS) app. This will initially be authenticating with the Solid Server. And then making requests to my Application Server. Authentication has to take place here because this will do the redirection to a browser (again, on iOS) so the user can enter their credentials. I’m basing my work here on GitHub - wrmack/Get-tokens: Displays retrieval of provider's configuration, registration of client, authentication and retrieval of tokens..
c) Application Server: My application that will be making content requests of the Solid Server.
I’ll be generating the public/private key on my Client. It needs it to authenticate with the Solid Server. It sounds like I’ll also have to pass the private/public key to my Application Server, so it can generate requests.
Storing data privately on my Application Sever isn’t a problem. But I’m not quite understanding the idea of “If you can’t store things privately in the web application, then you need to generate a key for each user.” I would have assumed that each user would need their own public/private key. But perhaps this is just me not knowing much about DPoP yet. So, the same public/private key can potentially be used across all users? Can you show me a reference in the DPoP docs so I can read more?
The “client” is usually the code that lives on your application server and in the client device, because both sides make requests to the authorization server and to the resource server (a Solid pod is the combination of these last 2 servers). It is useful to consider both sides of the application together, because you, the developer, control both sides. That is why standardization efforts like DPoP don’t make that distinction. So, “client” means the application, whatever form it takes and wherever it is executed. Some client applications only store data about the users locally, on the user’s device, so as not to require a database and a dynamic server. That is not your case, as I understand it.
As an application developer, you need to ask this question: where should the key pair be stored? Should it live on the server side of your application, or on the user side, in the browser storage?
If it is stored in the browser, you need to have a key per user. For you, the application developer, it means that you take the risk that the key is leaked from the web browser by some malware along with the access token, and then the attacker could impersonate your user. For the user, there’s little benefit, because since you control the code of the program, you can send the key to the server-side and share it with anyone you fancy, so having it stored on the browser does not mean for the user that it will stay there. However, for static applications that don’t have a server side (or the server side is just sending the source code to run on the browser and some CSS), this is the only option.
If it is stored as a secret on the server side, then you can use the same key for every one of your users and sign the requests on the server side. This is more comfortable for you, the developer, because you know the key is not floating somewhere in an insecure web browser, you can more easily track what request you signed, and you can make sure that the key is changed regularly.
I must add that iOS and the libraries that it uses has particularly weak security because most of the code is proprietary, so you don’t know if there are security flaws, you rely on Apple to fix them in time, and you don’t know what Apple is doing behind the scenes with the user’s private data.
Thank you. That clarifies some concepts. It does indeed feel more comfortable to me to keep the key pair on my server. And reading more, I am seeing that the steps up to and including the redirection where the user is asked to sign in (step 11 in Solid OIDC Primer) do not require use of the key pair. (I had mis-read before and thought that to setup the redirection, you had to have the key pair and use DPoP).
I am starting to feel better about this now
I do have a lingering question, however. I will be making use of a refresh token on my server. And I would like to also make use of a refresh token on my client. This is needed on the server because (for application specific reasons), the server needs to be able to access the users content data (i.e., in the Solid Server) in an ongoing manner. I’m assuming that’s feasible with my usual understanding of what a refresh token does, but I’ll have to read more to confirm that. That is, I’m assuming that a refresh token can be used on my server to generate an updated access token. It will have the key pair so I’m assuming this will just be another request. And draft-ietf-oauth-dpop-03 confirms this assumption.
My client also has need to use a refresh token. i.e., my client needs an updated access token. I should explain at this point that my server uses the specific client credentials to do authentication. E.g., if the client signs with Google Sign In, it passes that Google access token to the server. The server then checks with Google to make sure the access token is valid. It would seem then that the key pair is needed on my client in order to make a request to Solid to get an updated access token. It seems like my server has to send the key pair to my client on the initial sign in. Which seems less than satisfactory.
It’s sounding like the style of the mechanism here is analogous to that of Apple Sign In. With Apple Sign In, I cannot at all easily get a refreshed access token on my client. Though, there I can rely on an app-launch client check to make sure the user is still valid (getCredentialState). Without the key pair on my client I’m not sure I can do anything analogous to
getCredentialState with Solid.
It would seem then that the key pair is needed on my client in order to make a request to Solid to get an updated access token. It seems like my server has to send the key pair to my client on the initial sign in. Which seems less than satisfactory.
You could keep the ID token, access token and refresh token in a users database, and run every request from the server side of your application. You are bound to do this for when the user is not on the app anyway. However I agree that it’s a problem with DPoP. Namely, if the key was not bound to the refresh token, you could put the key in the client because it would expire in approximately 30 minutes (I think that’s the default configuration for Solid), so harm would be limited in case of a leak.
Thanks. I think what’s going on here is that I’m running into the limits of the architecture I’ve been using on my server for doing initial authentication per request (to my server). I’ve been using GitHub - Kitura/Kitura-Credentials: A pluggable framework for validating user credentials in a Swift server using Kitura based plugins which effectively don’t give that initial authentication layer access to the server database. With Facebook, Dropbox, and Google signins, this works fine. However, it doesn’t work well with Apple Sign In, and now with Solid. I may have to rethink that architecture.