question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add access to refresh_token in Microsoft.Identity.Client.AuthenticationResult

See original GitHub issue

Is your feature request related to a problem? Please describe. I need to use IPublicClientApplication.AcquireTokenWithDeviceCode(…) to handle authentication on some UI limited devices. These devices are running legacy code which requires both the access_token and the refresh_token. I can get the AuthenticationResult.AccessToken but there is no AuthenticationResult.RefreshToken. Using fiddler I can see the response from the server returns both an access_token and a refresh_token but for some reason the refresh token is not exposed in the result and I don’t see an easy way to get to it. I need to pass both of these tokens off to the legacy code so the system can work as it always has.

Describe the solution you’d like The easiest solution I’d like to see is to add a RefreshToken property to AuthenticationResult. Then its a matter of just handing the AuthenticationResult.AccessToken and AuthenticationResult.RefreshToken off to the legacy code and I’m done.

Or if there is a way to get at the refresh_token and someone can show me how to do it that would be appreciated as well.

IPublicClientApplication app = PublicClientApplicationBuilder
        .Create(AppSettings.ClientId)
        .WithTenantId(AppSettings.TenantId)
        .Build();

DeviceCodeFlow oauth = new DeviceCodeFlow(app);

AuthenticationResult result = await oauth.AcquireTokenAsync(new[] { "...scopes..." }, deviceCodeCallback =>
{
    // Prints message instructing user to go to https://microsoft.com/devicelogin and enter device code
    Console.WriteLine(deviceCodeCallback.Message);
    return Task.FromResult(0);
});

CallLegacyCodeWithTokens(result.Account.Username, result.AccessToken, result.RefreshToken);

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:39 (9 by maintainers)

github_iconTop GitHub Comments

6reactions
MatLegercommented, Jun 27, 2019

The easiest solution I could find to get around this problem is to create my own refresh token DelegateHandler and inject that into the HttpClient pipeline and execute a lambda when the refresh token comes back in a response. Now I’m able to pass the refresh token to the OneDriveClient and keep the legacy code as is. I still feel I should not have to do this and the refresh token should be part of the AuthenticationResult.

public async Task TestAuthenticationAsync()
{
    string refreshToken = string.Empty;

    IMsalHttpClientFactory httpClientFactory = new HttpClientFactory(new RefreshTokenHandler(responseRefreshToken => refreshToken = responseRefreshToken));

    IPublicClientApplication app = PublicClientApplicationBuilder
                                    .Create(AppSettings.ClientId)
                                    .WithTenantId(AppSettings.TenantId)
                                    .WithHttpClientFactory(httpClientFactory)
                                    .Build();

    DeviceCodeFlow oauth = new DeviceCodeFlow(app);

    AuthenticationResult result = await oauth.AcquireTokenAsync(new[] { "Files.ReadWrite.All" }, deviceCodeCallback =>
    {
        Console.WriteLine(deviceCodeCallback.Message);
        return Task.FromResult(0);
    });

    Console.WriteLine($"RefreshToken: {refreshToken}");
}

public class HttpClientFactory : IMsalHttpClientFactory
{
    private HttpMessageHandler MessageHandler { get; set; } = null;

    public HttpClientFactory(HttpMessageHandler messageHandler)
    {
        MessageHandler = messageHandler;
    }

    public HttpClient GetHttpClient()
    {
        return MessageHandler != null ? new HttpClient(MessageHandler) : new HttpClient();
    }
}

public class RefreshTokenHandler : DelegatingHandler
{
    Action<string> OnProcessRefreshToken { get; set; }

    public RefreshTokenHandler(Action<string> processRefreshToken) : this(new HttpClientHandler(), processRefreshToken)
    {
    }

    public RefreshTokenHandler(HttpMessageHandler innerHandler, Action<string> processRefreshToken)
    {
        InnerHandler = innerHandler;
        OnProcessRefreshToken = processRefreshToken;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        if (response.IsSuccessStatusCode && response.RequestMessage.RequestUri.AbsolutePath.EndsWith("/oauth2/v2.0/token"))
        {
            string json = await response.Content.ReadAsStringAsync();
            if (!string.IsNullOrEmpty(json))
            {
                JObject result = JsonConvert.DeserializeObject(json) as JObject;
                if (result != null)
                {
                    JToken tokenValue = null;

                    if (result.TryGetValue("refresh_token", out tokenValue))
                    {
                        OnProcessRefreshToken(tokenValue.ToString());
                    }
                }
            }
        }

        return response;
    }
}
6reactions
jmprieurcommented, Jun 26, 2019

@MatLeger MSAL.NET (and MSALs in general) don’t expose the refresh tokens as they handle refreshing tokens automatically when you call AcquireTokenSilent. Refreshs may become obsolete when you use them, and therefore the code using them could easily break.

Can we understand a bit better what your legacy code with Token does? and why it needs the refresh tokens? would passing an IPublicClientApplication to get a token using app.AcquireTokenSilent() be good as well?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Refresh tokens in the Microsoft identity platform
A client can use a refresh token to acquire access tokens across any combination of resource and tenant where it has permission to...
Read more >
Azure AD login using C#. Acquiring Refresh token and ...
My solution part: I use OAuth to connect to Azure AD. As OAuth client I use Microsoft.Identity.Client. For the first login I have...
Read more >
Acquire and cache tokens with Microsoft Authentication ...
Access tokens enable clients to securely call web APIs protected by Azure. There are several ways to acquire a token by using the...
Read more >
Get-MsalToken.ps1 4.2.1.1
Force interactive authentication to get AccessToken (with MS Graph permissions User.Read) and IdToken for specific Azure AD tenant and UPN using client id...
Read more >
AuthenticationResult.getRefreshToken - Java
Best Java code snippets using com.microsoft.aad.adal4j. ... logMessage = String .format("Access Token with hash '%s' and Refresh Token with hash '%s' ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found