Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Authorizing Access to Resources using Azure Active Directory

In my previous post I discussed authenticating a user using Azure Active Directory (Azure AD), returning an id_token that can be used to identify the user that has signed in. However, this token isn’t an access token and as such can’t be presented in order to access remote services. In this post I’m going to show how you can request an access token that can be presented in the Authorization header when calling a service. The workflow is very similar to the workflow to retrieve the id_token:

- User attempts to sign into an application

- Application launches the Authorize URL in an external browser (includes “resource” parameter in Authorize URL)

- User is directed to a Microsoft URL where they are prompted to sign in

- User signs in

- User is prompted to consent that the application can access the specified resource

- After sign in, the User is redirected back to the application via a custom protocol

- Application receives an authorization code

- Application performs a POST request to the Token URL in order to exchange authorization code for an access token

- Application receives the access token

- Application makes call to remote service, attaching the access token in the Authorization header

To demonstrate this workflow I’m going to adapt the sample application I created in my previous post in order to access the Azure Graph API. The sample application is already configured with access to the Azure Graph API – this is done by default for all application registrations. In order to request access to a resource, you need to know the url of the resource you want to access. In this case the url for the Azure Graph API is https://graph.windows.net.

In addition to including the resource url in the authorization url, the other change I need to make is to switch the response type from id_token to code. The updated authorization url is:

var  authorizationUrl=
    "https://login.microsoftonline.com/nicksdemodir.onmicrosoft.com/oauth2/authorize?" +
    "client_id=40dba662-4c53-4154-a5cf-976473306060&" +
    "response_type=code&" +
    "redirect_uri=sample://callback&" +
    "nonce=1234&" +
    "resource=https://graph.windows.net";

Launching this url in the external browser will again prompt the user to sign in (unless they have previously signed in for this application, such as if you followed my previous post) but rather than immediately being redirected back to the application, the user will see a consent prompt – you’ll note that similar to the sign in prompt, the name of the application specified in Azure AD is used when requesting permissions.

image

The other thing to note is that once you’ve consented permissions for the application, you won’t be prompted again – Azure AD remembers that you’ve granted permissions. Permissions can be revoked by going to https://myapps.microsoft.com, selecting the application and clicking Remove.

image

The same “Remove” option is available if you turn on the new look (it’s the same url to get there – https://myapps.microsoft.com)

image

After completing the sign in and consent workflow, the user is navigated back to the application using the custom protocol. This time, instead of an id_token, the application receives a code as part of the url:

sample://callback/?code=AQABAAAAAADRN…….L7YiQ7PIAA&session_state=ffffd83b-3820-489e-9f35-70e97d58fd04

Unlike the id_token, and as you’ll see soon, the access_token, the code is not a jwt token that you can interrogate for information about the signed in user. Instead the next step in the process is to do the code-token exchange to retrieve the required access token. This involves doing a POST request to the Token URL, passing parameters in the body of the request.

Token URL: https://login.microsoftonline.com/{tenantId}/oauth2/token

eg https://login.microsoftonline.com/nicksdemodir.onmicrosoft.com/oauth2/token

The POST body needs to contain the following values:

var postBody = new Dictionary<string, string>
{
    {"grant_type", "authorization_code"},
    {"client_id", "40dba662-4c53-4154-a5cf-976473306060"},
    {"redirect_uri", "sample://callback"},
    {"resource", "https://graph.windows.net"},
    {"code", code}
};

40dba662-4c53-4154-a5cf-976473306060 – This is the client id (aka application id) of the application registration in Azure AD

sample://callback – This is the redirect uri specified in the application registration

https://graph.windows.net – This is the resource that you’re requesting an access token for. Make sure this is the URL for the Azure Graph API, not to be confused with the Microsoft Graph API (https://graph.microsoft.com)

code – This is the actual code that is returned from the sign in process (ie don’t use the word “code”)

The resulting code for parsing the code from the application redirect, and then exchange for an access token is as follows:

protected override async void OnActivated(IActivatedEventArgs args)
{
    base.OnActivated(args);
    if (args.Kind == ActivationKind.Protocol)
    {
        var eventArgs = args as ProtocolActivatedEventArgs;

        var uri = eventArgs.Uri.AbsoluteUri;
        var code = uri?
                    .Split('?').Skip(1).FirstOrDefault()?
                    .Split('&').Select(q => q.Split('='))
                            .Where(x=>x.Length==2 && x[0]=="code")
                            .Select(x=>x[1])
                            .FirstOrDefault();

        var tokenUrl = "https://login.microsoftonline.com/nicksdemodir.onmicrosoft.com/oauth2/token";
        var postBody = new Dictionary<string, string>
        {
            {"grant_type", "authorization_code"},
            {"client_id", "40dba662-4c53-4154-a5cf-976473306060"},
            //{"redirect_uri", "sample://callback"},
            {"resource", "
https://graph.windows.net"},
            {"code", code}
        };
        using (var client = new HttpClient())
        {
            var content = new FormUrlEncodedContent(postBody);
            var result = await client.PostAsync(tokenUrl, content);
            var resultContent = await result.Content.ReadAsStringAsync();
        }
    }
}

The resultContent variable will include a JSON string which consists of an access_token, refresh_token etc.

image

In order to extract the access_token, the resultContent can be deserialized to an entity:

public class TokenData
{
    public string expires_in { get; set; }
    public string access_token { get; set; }
    public string refresh_token { get; set; }
    public string id_token { get; set; }
}

var resultContent = await result.Content.ReadAsStringAsync();
var token =  JsonConvert.DeserializeObject<TokenData>(resultContent);

Finally, the token.access_token value can be used to make a call to the Azure Graph API:

var graphUri = "https://graph.windows.net/me?api-version=1.6";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token.access_token);
var graphProfile = await client.GetStringAsync(graphUri);

The following screenshot shows the resulting profile information that’s returned from the Graph API

image

Note that the access token is relatively short lived. The refresh token can be used to renew the access token or to retrieve an access token for an alternative resource.

Pingbacks and trackbacks (1)+

Comments are closed