Receive custom parameter in token endpoint and add to access token #7
Replies: 5 comments
|
Sorry for the late response to this. The client claims are (as the name implies) tied to the client. The profile service is all the claims of the user. Claims that are tied to the client are unrelated to the user claims and thus is not visible/relevant for the profile service. A custom token request validator is meant as an extension point and can be used for this scenario. Please remember that the device_id is part of user input that is untrusted. Especially if the client is public (i.e. has no client secret) then anyone can send in a request with any device_id and have that end up in the token. |
It's obvious when you think about it :)
Great, that's what we settled on!
Yes, we tried to figure out the least bad option... The client is indeed public. Do you have a better suggestion for us how to add extra parameters to the token? We couldn't really find a "bombproof" solution in the docs and thought this would at least be the best of all bad ideas we had. |
|
(note: we're moving this issue to our new community discussions) |
|
Sorry, there's no one-size-fits-all answer to this. The solution is very specific for your use case. I would suggest a consultancy session to discuss the specific details so we can come up with a suitable, secure solution. @AndersAbel is available in Sweden for this if needed. |
|
@OskarKlintrot FYI, I had a similar requirement, and this is what I ended up doing based on various sources across the web. It successfully adds the claim to the tokens as expected and does not require us to tie a claim to a client. One possible downside to mention: This only adds the claim to the grant (so any tokens generated by the grant, including the refresh token). If you have server-side sessions enabled, this does NOT update the ticket stored in the server-side session. What this means is that if you try to call the userinfo endpoint with server-side sessions enabled, this claim will NOT get returned by the userinfo endpoint. It WILL be preserved if you trade your refresh token in for another access token since it's stored with the grant (e.g. in the PersistedGrants table in the DB). Just sharing what I've come up with, YMMV. This is added to the custom token request validator. if (grantType == OidcConstants.GrantTypes.RefreshToken)
{
var incomingDeviceId = validatedRequest.Raw.Get("deviceId") ??
validatedRequest.Raw.Get("device_id");
if (String.IsNullOrWhiteSpace(incomingDeviceId))
{
Log.Warning("A device ID was not provided in the token request! This may be expected depending on the client and client version.");
}
else
{
Log.Debug("Device ID {DeviceId} was provided in the token request", incomingDeviceId);
var loginDeviceId = validatedRequest.Subject?.FindFirstValue(YourCustomClaimTypes.DeviceId);
if (String.IsNullOrWhiteSpace(loginDeviceId))
{
if (DeviceValidator.IsValidDeviceId(incomingDeviceId) == false)
{
Log.Error("Invalid device ID provided: '{BadDeviceId}'", incomingDeviceId);
// try this in production first before blocking... we don't want to break every refresh token currently in production on accident
// context.Result.IsError = true;
// context.Result.Error = "invalid_request";
// context.Result.ErrorDescription = "The provided information is not valid";
// return;
}
Log.Warning("No device ID has been set in the user identity session. Adding device ID {DeviceId} to user claims", incomingDeviceId);
if (validatedRequest.Subject?.Identity is ClaimsIdentity claimsIdentity)
{
claimsIdentity.AddClaim(new(YourCustomClaimTypes.DeviceId, incomingDeviceId));
}
else
{
Log.Error("Unable to add device ID to user claims: {Error}", "No claims identity found");
}
}
else
{
Log.Verbose("Attempting to validate provided device ID");
if (incomingDeviceId != loginDeviceId)
{
Log.Error("The device ID provided in the token request does not match the device ID provided during login! This is not expected behaviour!");
// try this in production first before blocking... we don't want to break every refresh token currently in production on accident
// context.Result.IsError = true;
// context.Result.Error = "invalid_request";
// context.Result.ErrorDescription = "The provided information is not valid";
// return;
}
}
}
} |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Which version of Duende IdentityServer are you using?
v7.0.7
Which version of .NET are you using?
.NET 8
Question
I want to receive a custom parameter in the token endpoint that I then add to the access token. It has nothing to do with the actual authentication so
acrseems to be the wrong place as well as parameterized scopes (but I might be wrong), hence why I just want to add a parameter, just likeIdentityModellets me;client.Parameters.Add("device_id", "1234abc");.Everything looks right in the request:
Then I tried to follow Dynamic Request Validation and Customization like this:
Which does add the claim to the client (might be the wrong place, the device id belongs to a user and not a client) before exiting the method but then it disappears. I can't find it later in my custom
IProfileService. Doesn't seem to matter if I request the scopedevice_idor not either (I added the scope to the client). I assume I'm doing something wrong but I'm out of ideas by now...Update
If I create a new
ClaimsPrincipaland add the claim there I can find it later in the profile service:Maybe this is more correct? I can also add it directly to the client (
context.Result.ValidatedRequest.Client.Claims.Add(new ClientClaim("device_id", deviceId));) to work with it later in the profile service. Not sure what is most correct.All reactions