How do I get Azure AD OAuth2 Access Token and Refresh token for Daemon or Server to C# ASP.NET Web API

13,696

Solution 1

When I posted this question, this was the answer I was looking for, please see screen shot below for expected result and c# console solution. Having found the solution, it is worth sharing it here, may be useful to someone some day

C# console app code to achieve expected result in the postman screen shot below

using System;
using System.Collections.Generic;
using System.Net.Http;

namespace AzureADTokenApp
{
    class Program
    {
        static void Main(string[] args)
        {

            var client = new HttpClient();
            var uri = "https://login.microsoftonline.com/<tenant-name>.onmicrosoft.com/oauth2/token?api-version=1.0";
            var pairs = new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("resource", "https://graph.microsoft.com"),
                new KeyValuePair<string, string>("client_id", "<azure ad client id e.g. 9b864-a5e6-4f0d-b155-1f53a6c78>"),
                new KeyValuePair<string, string>("client_secret", "<azure ad client secret e.g. MTMiXaO1P9HnhSawdXWmcnuQ="),
                new KeyValuePair<string, string>("grant_type", "password"),
                new KeyValuePair<string, string>("username", "<azure ad user e.g. [email protected]>"),
                new KeyValuePair<string, string>("password", "<azure ad user password e.g. Pa$$word01>"),
                new KeyValuePair<string, string>("scope", "openid")
             };

            var content = new FormUrlEncodedContent(pairs);

            var response = client.PostAsync(uri, content).Result;

            string result = string.Empty;

            if (response.IsSuccessStatusCode)
            {

                result = response.Content.ReadAsStringAsync().Result;
            }

            Console.WriteLine(result);
            Console.ReadLine();
        }
    }
}

Screenshot from Postman - Expected Result. You will have same result in console except is less readable

enter image description here

Solution 2

You are using the client credentials flow. In that flow, a refresh token should not be included https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3. Also it looks like you are using ADAL v3, which anyways doesn't return refresh tokens (by design), but it uses them automatically for you. More info here http://www.cloudidentity.com/blog/2015/08/13/adal-3-didnt-return-refresh-tokens-for-5-months-and-nobody-noticed/

Solution 3

There is no need to use the refresh token to redeem to access token manually if you were developing with ADAL v3.

In this scenario, we should use the AcquireTokenSilentAsync and catch the exception. After the access_token is expired, we should re-initialize AuthenticationContext and call the AcquireTokenAsync again to send the request to acquire the access_token.

Here is a code sample for your reference:

class Program
{
    static string authority = "https://login.microsoftonline.com/adfei.onmicrosoft.com";
    static string resrouce = "https://graph.microsoft.com";
    static string clientId = "";
    static string secret = "";
    static ClientCredential credential = new ClientCredential(clientId, secret);
    static AuthenticationContext authContext = new AuthenticationContext(authority);
    static void Main(string[] args)
    {
        while (true)
        {
            var client = new GraphServiceClient(new DelegateAuthenticationProvider(request =>
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("bearer", GetAccessToken());
                return Task.FromResult(0);
            }));

            var users = client.Users.Request().GetAsync().Result.CurrentPage;
            foreach (var user in users)
            {
                Console.WriteLine(user.DisplayName);
            }          
            Thread.Sleep(1000 * 60 * 5);
        }
        Console.Read();
    }

    static string GetAccessToken()
    {
        try
        {
            var token = authContext.AcquireTokenAsync(resrouce, credential).Result.AccessToken;             
            return token;
        }
        catch (Exception ex)
        {
            authContext = new AuthenticationContext(authority);
            return GetAccessToken();
        }
    }
Share:
13,696
Julius Depulla
Author by

Julius Depulla

Updated on July 28, 2022

Comments

  • Julius Depulla
    Julius Depulla almost 2 years

    I have implemented an Azure AD OAuth2 Daemon or Server to ASP.NET Web API. However I only receive an access token which is the property on the AuthenticationResult. See implementation below.

        public IHttpActionResult GetAccessToken(string clientId, string clientkey)
        {
            AuthenticationContext authContext = new AuthenticationContext(authority);
            ClientCredential clientCredential = new ClientCredential(clientId, clientkey);
            AuthenticationResult authenticationResult = authContext.AcquireTokenAsync(resourceUri, clientCredential).Result;
            Authorisation authorisation = new Authorisation {access_token = authenticationResult.AccessToken,
                                                    token_type = authenticationResult.AccessTokenType,
                                                    expires_on = authenticationResult.ExpiresOn };
    
            return Ok(authorisation);
        }   
    

    This returns only access token. I would like an implementation, a Daemon or Server implementation that returns both access token and refresh token. Have your seen or done similar implementation. Any useful links to an example are welcome.