2

I am using Duende Identity server and I have an external authentication provider lets say google. While logging into google we get tokens from google which we can make use of calling some google API's.

I need to return the google token also to the client side(Angular/WPF/MVC etc) through Duende token endpoint.

I can see from the code that Duende token endpoint response has a Custom property, but I have no clue how or from where I can insert my values.

From Duende Source Code

internal class ResultDto
{
    public string id_token { get; set; }
    public string access_token { get; set; }
    public int expires_in { get; set; }
    public string token_type { get; set; }
    public string refresh_token { get; set; }
    public string scope { get; set; }

    [JsonExtensionData]
    public Dictionary<string, object> Custom { get; set; }
}

I would like to see some code snippets or direction on how to add values to this Custom property by existing Duende functionality.

MD Zand
  • 2,366
  • 3
  • 14
  • 25
Kiran B
  • 683
  • 10
  • 21
  • 1
    Please add codes as text in the questions/answers and avoid adding images for this purpose.Please read [this](https://stackoverflow.com/help/formatting) – MD Zand Jan 12 '23 at 08:01
  • Why don't you just add what ever needed to claims in `Callback` ? – MD Zand Jan 12 '23 at 09:12
  • @MDZand Its one of the options we have, but at which extension point I can get the external token and add it to the claims so that it will be part of the issued JWT. Again I am truly looking for customizing the token response as I need to add more items from external provider than just the access_token. – Kiran B Jan 12 '23 at 09:39

2 Answers2

2

If you need to customize token response you can ICustomTokenResponseGenerator (It is for identity server 3, if you are using version 4 and above I am not sure but it should be ITokenResponseGenerator):

class CustomTokenResponseGenerator : ICustomTokenRequestValidator
{
    public Task<TokenResponse> GenerateAsync(ValidatedTokenRequest request, TokenResponse response)
    {
        response.Custom.Add("custom_field", "custom data");      
        return Task.FromResult(response);
    }
}

and then add it with factory:

 factory.CustomTokenResponseGenerator = new Registration<ICustomTokenResponseGenerator, CustomTokenResponseGeneratorService>();
MD Zand
  • 2,366
  • 3
  • 14
  • 25
  • Hi MD Zand I am using Duende which is a newer version of old Identity Server, but I think the solution you have provided will work for both, let me see if I can implement this and solve the issue. I can update you. Thanks – Kiran B Jan 12 '23 at 10:14
  • @KiranB How did it go? I'm not clear on the `factory` part. They say at [Duende's docs](https://docs.duendesoftware.com/identityserver/v6/reference/response_handling/token_response_generator/) that we can do what MD proposes. However, I'm very confused as to where the factory to inject into comes from. – Konrad Viltersten May 18 '23 at 15:44
  • Hi @KonradViltersten I have done the implementation and it works for me. I can write down the steps as an answer by today. Hope that helps you – Kiran B May 19 '23 at 07:21
  • 1
    @KiranB That would be awesome. You could, in fact, post that as an answer to [my question](https://stackoverflow.com/questions/76282243/how-to-add-custom-claim-to-a-token-in-duende-under-client-credential-flow), if you wish. – Konrad Viltersten May 19 '23 at 19:59
1

The answer from MD Zand was helpful and it helped me to solve the issue, but it was related to Identity server and not Duende. In the case of Duende the interfaces and classes to inherit are different.

internal class CustomTokenResponseGenerator : TokenResponseGenerator
{
    public CustomTokenResponseGenerator(ISystemClock clock, ITokenService tokenService, IRefreshTokenService refreshTokenService, IScopeParser scopeParser, IResourceStore resources, IClientStore clients, ILogger<TokenResponseGenerator> logger) : base(clock, tokenService, refreshTokenService, scopeParser, resources, clients, logger)
    {
    }
    protected override async Task<TokenResponse> ProcessAuthorizationCodeRequestAsync(TokenRequestValidationResult request)
    {
        var result = await base.ProcessAuthorizationCodeRequestAsync(request);

        if (result != null)
        {
            //using this user we can get the external token form google, facebook etc
            
            var user = request.ValidatedRequest.Subject;                

            //I have added google token to the user claims when I got the token from google. 
            //Instead we can add it to database and make a DB call here if you want to persist
            var externalTokenResponse = user.Claims.FirstOrDefault(s => s.Type == "ExternalToken")?.Value;

            if (!string.IsNullOrEmpty(externalTokenResponse))
            {
                if (result.Custom == null)
                {
                    result.Custom = new Dictionary<string, object>();
                }
                result.Custom.Add("ExternalToken", JsonDocument.Parse(externalTokenResponse));
            }
        }

        return result;
    }
}

Once we have defined the above implementation we can register to DI using below code

builder.Services.AddTransient<ITokenResponseGenerator, CustomTokenResponseGenerator>();

The above will override the default implementation of Duende.

Kiran B
  • 683
  • 10
  • 21