Inrupt Dynamic Client Registration

Hello,

I’m working with the SOLID OIDC Dynamic Credentials registration and I’m stuck on the Token authentication step? The JWT I’m constructing is verified on jwt.io and has all the fields that appear required to match the token endpoint requirements, so I believe there is something wrong with the body of the request.

My body currently looks like as follows (newlines are for clarity)

grant_type=authorization_code
&code_verifier=tEsTCo!de?123
&code=random-uuid-v4
&redirect_uri=app%3A%2F%2Fmy-test-redirect.com%2Fcallback
&client_id=dynamic-id-from-registration-response
&client_secret=secret-from-registration-response

Is there any encoding that needs to happen for these, and does the client ID have to include the base URI of my app + the client_id, or is the client_id by itself valid? I was not able to determine from the OIDC documents which decision was correct.

1 Like

Hi,
I’m sorry, I’m not sure I’m following exactly what you are trying to do. What are you implementing exactly, a client piece of code performing the OpenID Authorization Code Grant (aka Code flow)?

One thing maybe, the Authorization Request doesn’t typically include the Client Secret: Final: OpenID Connect Core 1.0 incorporating errata set 1. The Client credentials are presented during the Token Request, after the client has obtained an Authorization Code.

And also, note that none of this is Inrupt-specific :slight_smile: . We have developed a library that implements an OpenID client, but this library either implements parts of the OpenID specification suite, or it implements the Solid-OIDC specification.

1 Like

Hello,

Yeah, I already registered the dynamic client at the registration endpoint and received a client secret and client ID. I also received the code in the 302 redirect as well and formatted the request with a JWT that passes the JWT.io spec. I’m reasonably sure I packed them properly and set the content type and formatted the body, but I’m received a 401 invalid client credentials error. It seems to have something to do with the redirect uri - which is of the format app://my-oidc-component.com/callback .

I tried the client secret since the authorization_Code request type seems to use it, but that still returned a 401. What format does the client id have to be in? Just the value returned from the registration endpoint, or a uri + value?

What format does the client id have to be in? Just the value returned from the registration endpoint, or a uri + value?

You’re correct, the value returned by the registration endpoint should be all you need.

I also received the code in the 302 redirect as well and formatted the request with a JWT that passes the JWT.io spec.

Could you provide details of the requests/responses you’re exchanging with the server? In particular, I’m not sure why you are creating a JWT: if you’ve successfully obtained the authorization code after the redirect to your application, you’re at the Token Request step, as described by OpenID and more fundamentally OAuth. The data set by the client to the OpenID Provider’s Token Endpoint should be formatted as application/x-www-form-urlencoded, and the client authentication is done either using the Basic authentication scheme or Client Secret post. Your initial message hints that you’re doing the latter, so I’m not sure where a JWT comes into this.

So I am making a client app, which allows the user to input their webId uri and then parses the solid:oidcIssuer predicate and object out. which takes me to step 4. in the OIDC Primer. Next I generate the code and code verifier then send the auth request back to login.inrupt.com or whatever. I get the code at the redirect url in step 11. So 12 and 13 are when I generate the dpop+jwt keypair and then I try and send the token request and code verifier so I am sending a content-type application/x-www-form-urlencoded header and the DPoP header with the jwt, then the body as described. I’ve managed to get the static credentials flow working with CSS before, but this is using the dynamic client registration with Inrupt.

Aah, I did not realize you were doing DPoP binding, this clarifies things thanks! I just checked https://login.inrupt.com/.well-known/openid-configuration, and I stand corrected: the client_secret_post method is supported by the token endpoint, so including the client_secret in the request body should be correct. You should also be able to send it using the Basic authentication scheme, namely putting Basic: <Base-64 encoded string concatenating client_id:client_secret> in the Authorization header. Note that doing the latter over the former is recommended by the OAuth 2.0 spec.

Another thing, which may not be the root of the issue but is worth experimenting, is that DPoP binding isn’t mandatory. Could you try without a DPoP header at all to see if you’re observing a similar issue?

Otherwise, what you describe sounds correct, so I’m not sure where the issue is coming from. Dynamic client registration is supported by https://login.inrupt.com. Would you have an example network log to have a closer look?

Without the DPoP binding you mean just sending a request with the Basic Auth header and the grant_type body as well? The dynamic registration shouldn’t be an issue but it may be something to do with my uri, as I’m using it from a native application, so it is not an https redirect it is an app:// redirect.

Without the DPoP binding you mean just sending a request with the Basic Auth header and the grant_type body as well?

Exactly, binding to a DPoP key is an additional security measure to mitigate risks of token replay by a malicious resource server, so it’s good to have it, but it’s worth seeing if that impacts anything.

The dynamic registration shouldn’t be an issue but it may be something to do with my uri, as I’m using it from a native application, so it is not an https redirect it is an app:// redirect.

If the redirect URL was an issue, I’d expect the authorization request to fail, rather than a 401 response to the Token Request. As long as the redirect URL matches what has been registered during client registration, that shouldn’t be a problem.

You’re using a code verifier (PKCE), if misused I could see the server responding 401. Are you sure it is properly used? For the sake of experiment, you can also not use PKCE, and stick to the basic Authorization Code grant, and make sure it works, before adding additional security (which is a good thing to have, but adds some complexity).

I removed the DPoP header and used The base 64 url safe encoded username:password combination in the Authorization header. (Authorization: Basic myB64Uuser:pass). I also removed the PKCE code challenge. The body of the request is just grant_type, code, and redirect_uri. It is still returning a 401 error. This is what the network requests are showing.
image

This is the openid-configuration response

This is the registration response

In the network log you are showing, there is no call to the Authorization Endpoint, where the user should be redirected. Going directly to the Token Endpoint is something you can do for the Client Credentials flow, which is not compatible with Dynamic Client Registration (you need to statically register the client at Application Registration).

In our conversation so far, my understanding was that you were doing the authorization code flow, in which case the network log I’d expect would be something like:

  • OpenID configuration discovery
  • Dynamic Client Registration
  • Authorization Request
  • Token Request

Is my understanding correct?

I am doing the Auth request but it isn’t being captured by the Network inspector because it is a native application using a webview rather than a raw request, which was recommended to do by OIDC protocols. The Auth request has to be working, as it was how I am getting the “code” parameter in the callback, unless I am misunderstanding a step here.

Below is the data I am sending to the auth endpoint and getting back (yellow line is the token endpoint response)

1 Like

Ah, I see, thanks for the explanation.

The Auth request has to be working, as it was how I am getting the “code” parameter in the callback, unless I am misunderstanding a step here.

That sounds correct.

I’m sorry, but at the moment I’m not sure what is causing the issue you are experiencing. The process you are following sounds correct, and if your request to the token endpoint is authenticated using the Basic authentication scheme with the credentials issued by the dynamic registration endpoint, and if the redirect_url are matching between the authz request and the token request nothing odd jumps out at me. Dynamic Client Registration is definitely supported by this OpenID Provider, and I don’t see why a native redirect URl should be an issue, but I’ll experiment some things on my end and see if I find any element of response.

1 Like

That’s unfortunate to hear. If you need me to help with something related to this or need more situation about my setup, don’t hesitate to ask. I’m more than happy with the libraries you’re using since I’ve peeked at the Inrupt github libraries every so often.

I’m sorry to revive this thread, but I managed to fix the issue with a line of code, but haven’t gotten around to integrating DPoP.

For the UMA portion, since I only used client secret basic with the Basic auth header, is that what needs to be provided in the UMA endpoint request + claim token instead of the DPoP tokens, and the rest of the flow should progress smoothly?

Reviving a thread is absolutely fine, I’m glad you found a fix, can I ask what the issue was? And what is the issue you are still experiencing with DPoP?

And yes, that’s all you should need in the UMA claims gathering.

I’m almost certain it was due to the fact I was not including the “token_endpoint_auth_method” in the registration request. The documentation I was reading did not say it was a required field so I was not originally including it.

Nothing is wrong with the DPoP fields yet to my knowledge. The library I am using does not support them natively and does not have an interface to include them that I have seen yet, so I may have to construct them from scratch is all.

For the UMA process, is the claim_token field fulfilled by the access_token or the id_token field returned from the auth server, and does the id_token or access_token need to be included as the rpt field?

I have the access token from the auth token endpoint and an access token from the uma token endpoint, but I am unsure what the actual request for the resource uri is supposed to look like. Does it need two separate DPoP headers?

For the UMA process, is the claim_token field fulfilled by the access_token or the id_token field returned from the auth server, and does the id_token or access_token need to be included as the rpt field?

An UMA Authorization server may support multiple claim_token_types, that build up into the access attached to the RPT (the access token issued by the UMA token endpoint). Unless these are covered by a specification, which isn’t currently the case, the specific claim tokens will be use-case specific, so the rest of what I’m going to say only applies to the Inrupt implementation of the UMA protocol as available on Podspaces for instance. The access_token from the OpenID Provide Token endpoint is orthogonal to UMA, only the ID Token will be a good claim token to be pushed to the UMA Token Endpoint.

If you are pushing multiple claims into a single token (something absolutely standard in UMA), then you’ll want to include the UMA-issued RPT in the rpt field so that the UMA server upgrades it rather than issuing a new token. This is described in more details in User-Managed Access (UMA) 2.0 Grant for OAuth 2.0 Authorization.

The DPoP binding, as for the OpenID Provider, happens in at the Token Endpoint. You’ll only have one DPoP header, that will bind the UMA-issued RPT to the DPoP key (if supported by the UMA server). Once your RPT is bound to the DPoP key, the request to the resource works the same way as when you are using the access_token from the OpenID Provider.

Does that answer the question?

1 Like

I think I’m understanding. So the access token issued by the PodSpaces token endpoint is perfectly valid for using as a DPoP header and attaching to subsequent requests to edit a resource, while the UMA endpoint uses the ID token from the OIDC Token Endpoint to just upgrade the token instead?

I was asking because in the SOLID-OIDC primer it mentioned having to handle the UMA process once I had finished the token portion, and the access token from the OIDC Token endpoint was not being used transparently, and attempting authorized requests with the access token returned from the UMA endpoint was giving me 401 errors.