Adding AAD Service Principal to the Company Administrator Role using the AAD PowerShell Module

When creating a new Azure Active Directory application, developers may run into a a problem when calling the AAD Graph API where they lack the correct permissions to call the APIs they want when calling in the App Only Flow (Client Credentials Flow).

Is this message familiar to you?

"odata.error":{
    "code":"Authorization_RequestDenied",
    "message":{
        "lang":"en","value":"Insufficient privileges to complete the operation."
    }
}

The correct thing to do would be to try and investigate the permissions you have granted to your application, but there are some APIs which are not even supported through the permissions publicly exposed by the AAD Graph API. Maybe you just want to overcome this error for the time being and continue testing your end to end experience.

Using the AAD PowerShell Module, you can:

  • Give your application full access to the Graph API in the context of my tenant.

or

  • Grant your application permissions to my tenant which are not currently supported with the permissions exposed by the AAD Graph API.

“How?” you might ask. Well, you can elevate the level of access an Application has in your tenant by adding the service principal of that application to the Company Administrator Directory Role. This will give the Application the same level of permissions as the Company Administrator, who can do anything. You can follow these same instructions for any type of Directory Role depending on the level of access you want to give to this application.

Note that this will only affect the access your app has in your tenant.
Also you must already be a Company Administrator of the tenant to follow these instructions.

 

In order to make the change, you will need to install the Azure Active Directory PowerShell Module.

Once you have the module installed, authenticate to your tenant with your Administrator Account:

Connect-MSOLService

Then we need to get the Object ID of both the Service Principal we want to elevate, and the Company Administrator Role for your tenant.

Search for Service Principal by App ID GUID:

$sp = Get-MsolServicePrincipal -AppPrincipalId <App ID GUID>

Search for Directory Role by Name

$role = Get-MsolRole -RoleName "Company Administrator"

Now we can use the Add-MsolRoleMember command to add this role to the service principal.

Add-MsolRoleMember -RoleObjectId $role.ObjectId -RoleMemberType ServicePrincipal -RoleMemberObjectId $sp.ObjectId

To check everything is working, lets get back all the members of the Company Administrator role:

Get-MsolRoleMember -RoleObjectId $role.ObjectId

You should see your application in that list, where RoleMemberType is ServicePrincipal and DisplayName is the name of your application.

Now your application should be able to perform any Graph API calls that the Company Administrator could do, all without a user signed-in, using the Client Credential Flow.

Let me know if this helps!

Common Microsoft Resources in Azure Active Directory

I have seen a lot of StackOverflow posts trying to debug pretty basic errors when getting an access token to Microsoft Resources. Sometimes the issue is as simple as a typo in the “resource” value in the token request. When helping these users, I struggle to find public documentation which shows plainly the correct resource values for these different APIs!

That is going to change starting now. Here will be a list of the most popular Microsoft APIs exposed on Azure Active Directory, along with the basic information you may need to get an access token to those resources for PROD. (If you want the details for other Environments, let me know!)

Note: The Resource URI must match exactly what is written below, including any trailing ‘/’ or lack thereof.

 

Resource Name Resource URI Application ID
AAD Graph API https://graph.windows.net/ 00000002-0000-0000-c000-000000000000
Office 365 Exchange Online https://outlook-sdf.office.com/ 00000002-0000-0ff1-ce00-000000000000
Microsoft Graph https://graph.microsoft.com 00000003-0000-0000-c000-000000000000
Skype for Business Online https://api.skypeforbusiness.com/ 00000004-0000-0ff1-ce00-000000000000
Office 365 Yammer https://api.yammer.com/ 00000005-0000-0ff1-ce00-000000000000
OneNote https://onenote.com/ 2d4d3d8e-2be3-4bef-9f87-7875a61c29de
Windows Azure Service Management API https://management.core.windows.net/ 797f4846-ba00-4fd7-ba43-dac1f8f63013
Office 365 Management APIs https://manage.office.com c5393580-f805-4401-95e8-94b7a6ef2fc2
Microsoft Teams Services https://api.spaces.skype.com/ cc15fd57-2c6c-4117-a88c-83b1d56b4bbe
Azure Key Vault https://vault.azure.net cfa8b339-82a2-471a-a3c9-0fc0be7a4093

Who knows if this will actually end up helping anyone, but I hope it will!

Refresh Tokens for Azure AD V2 Applications in Flask

I have been working on a few projects recently that used Flask, a Python web framework, and Azure Active Directory to do things related to the Microsoft Graph. Using flask_oauthlib and the Azure AD V2 endpoint, it has been really easy to set up basic authentication for my web apps.

However, we quickly ran into basic authentication headaches like token expiry. It seems pretty obvious to the end user that as long as they haven’t logged out since the last time they visited the site, they should stay automatically logged in. However, if you set up a naive implementation of authentication, you will find that the access token you store for the user is only valid for a limited time; by default just 1 hour.

So do we make our user sign in every hour?

Hell no. We need to use refresh tokens which can be exchanged for NEW access tokens, all without the user being asked to sign in again.

How do we get a refresh token?

In order to get a refresh token from the Azure AD V2 endpoint, you need to make sure your application requests a specific scope: offline_access. As stated here:

When a user approves the offline_access scope, your app can receive refresh tokens from the v2.0 token endpoint. Refresh tokens are long-lived. Your app can get new access tokens as older ones expire.

If your app does not request the offline_access scope, it won’t receive refresh tokens.

So how do we do it?

Unfortunately flask_oauthlib does not directly support refresh tokens, but it does support remote methods, so we should be able to simply make a POST request for a new access token! Here is the surprisingly simple code you need:

    data = {}
    data['grant_type'] = 'refresh_token'
    data['refresh_token'] = session['refresh_token']
    data['client_id'] = microsoft.consumer_key
    data['client_secret'] = microsoft.consumer_secret
    
    response = (microsoft.post(microsoft.access_token_url, data=data)).data

That’s it! What you are not seeing here is the original code used to get an access token, but I am just doing the normal flask_oauthlib stuff (which you can find in this sample), and storing the results of the token response in the user’s session (Access Token, Expires In, and Refresh Token).

Now, in order to invoke this code at the right time, I need to create a view decorator, and Flask has a sample for almost exactly what we want to do here: a login required decorator.

Basically, we add this decorator to any view where we expect the user to be signed in. If the user has no token, we will redirect them to the login page. If they have an expired token and a refresh token, we will use the refresh token to get a new access token. Otherwise, if the token is present and valid, we simply let the view load.

Check it out in full action here:

def login_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        if 'microsoft_token' in session:
            if session['expires_at'] > datetime.now():
                return f(*args, **kwargs)
            elif 'refresh_token' in session:
                # Try to get a Refresh Token
                data = {}
                data['grant_type'] = 'refresh_token'
                data['refresh_token'] = session['refresh_token']
                data['client_id'] = microsoft.consumer_key
                data['client_secret'] = microsoft.consumer_secret

                response = (microsoft.post(microsoft.access_token_url, data=data)).data
                
                if response is None:
                    session.clear()
                    print("Access Denied: Reason=%s\nError=%s" % (response.get('error'),request.get('error_description')))
                    return redirect(url_for('index'))
                else:
                    session['microsoft_token'] = (response['access_token'], '')
                    session['expires_at'] = datetime.now() + timedelta(seconds=int(response['expires_in']))
                    session['refresh_token'] = response['refresh_token']
                    return f(*args, **kwargs)
        else:
            return redirect(url_for('login'))
    return wrap

So now when we want to make a page require login we simply set up our view function like so:

@app.route('/home')
@login_required
def home():
    #view code goes here

Adding this kind of code to your Azure AD Flask application can really be the difference between a good and bad user experience. Let me know if this ends up helping you out!

Revoking Consent for Azure Active Directory Applications

Today I was presenting one of my hackathon projects which I worked on this year to the Identity team at Microsoft. In order for my project to work, I needed to get consent to read the mail of the signed-in user. Depending on who you talk to, a permission like this could be easy as pie to consent to or something that they would never accept. Some people fall in the middle where they are happy to consent as long as they can choose to revoke that consent after they are done playing with the app.

That is why I am writing this. How easy it is to forget that it is NOT very obvious what you need to do to revoke consent for an Azure Active Directory Application. Even people on the Identity team don’t always know! So let’s talk about how you can do it 🙂

Using the My Apps Portal for Individual User Consent

You can revoke individual user consent through the My Apps Portal. Here you should see a view of all applications that you or even your administrator (on your behalf) has consented to:

With applications your admin has consented to, all you can do is open the app, however for apps where you individually consented as a user, you can click “Remove” which will revoke consent for the application.

Using the Azure Portal to Remove Tenant Wide Consent

If you are a tenant administrator, and you want to revoke consent for an application across your entire tenant, you can go to the Azure Portal.  Whether it be for a bunch of users who individually consented or for an admin who consented on behalf of all the users, by simply deleting the application’s service principal, you will remove all OAuth 2 Permission Grants (the object used to store consent) linked to that application. Think about removing the service principal like uninstalling the application from your tenant.

You could delete the service principal a bunch of different ways like through Azure Active Directory PowerShell or through the Microsoft Graph API, but the easiest way for the average administrator is right through the Azure Portal.

Navigate to the Enterprise Applications blade in the Azure portal:

Then click “All Applications” and search for the application you want to revoke consent for:

When you click the application, you will be brought to an “Overview” section, where a tempting button called “Delete” will be at the top. Before you click this button,  you might want to take a peak at the “Permissions” section to see the types of consent that was granted to this application:

Once you feel confident that you want to delete this application, go back to “Overview” and click “Delete”!

Viola! The app and all consent associated with that app is now gone.

Clients and Tokens and Claims! Oh My!

Let me just jump to the point with this post: Client applications should not depend on claims in access tokens to gather data about the signed-in user or anything about the authenticated session.

Time and time again, I have seen client applications complain to me that certain claims, like group membership claims, are not appearing in the access token they receive, and they ask me how to enable this. They incorrectly assume that if they go into their application manifest, and change the “groupMembershipClaims” settings, that they will be able to start getting claims, but everyone eventually finds out… it doesn’t work!

Let’s take a look at source material; from the OAuth 2 specification:

An access token is a string representing an authorization issued to the client. The string is usually opaque to the client.

Unfortunately, the OAuth 2 specification is intentionally broad, but in summary, the ‘access token’ that is given to a client should only really be explored by the specified audience of the token. Some implementations of OAuth 2 do not even pass a JWT token to the client. Instead they pass a unique string, and then the resource exchanges that string for the actual token using a signed request. Alternatively, other implementations pass an encrypted JWT token rather than just a signed token. This means that the resource application uploads a token signing key which the authorization server uses to encrypt the full token. That means that the only person who can look at the claims in the token is the resource who also has the private key for decryption.

The implementation of OAuth 2 that I am most familiar with, Azure Active Directory,  issues a signed token, which means that its content is completely visible to the client. In the future, Azure AD may add support for encrypted tokens, which means that clients are going to have to start following the correct practices.

Need to know about the user signed into your web application?

>> Get an ID token! These are meant for client consumption!

Need to know which groups a user is a member of?

>> Get an access token to the AAD or Microsoft Graph API and query the API!

Now lets go back to the original problem. If groupMembershipClaims are not meant for clients to get the claims in the access token, what are they used for? You might have figured out by now, but they are for resource applications to get the claims in the access token!

Lets show an example. To set up, I have registered two Azure AD Web Apps/APIs called Web API 1 and Web API 2. Both of these applications are identical, except Web API 1 has the setting “groupMembershipClaims”: “All”, and the other is set to null, which is default. I have to set up a fake App ID URI for both apps, and I have to make sure that each application has the other set as a “required permission”.

I will be using my PowerShell Scripts to quickly get two access tokens. One where the client is Web API 1 and the resource is Web API 2, and vice versa.

Let’s look at the results, using my JWT Decoder to look at the payload:

Payload 1:  Client = Web API 1, Resource = Web API 2

{
    "aud": "https://shawntest.onmicrosoft.com/WebApi2",
    "iss": "https://sts.windows.net/4a4d599f-e69d-4cd8-a9e1-9882ea340fb5/",
    "iat": 1500243353,
    "nbf": 1500243353,
    "exp": 1500247253,
    "acr": "1",
    "aio": "ATQAy/.../oU",
    "amr": [ "rsa", "mfa" ],
    "appid": "eb7b6208-538c-487b-b5b5-137ac6ab6646",
    "appidacr": "1",
    "email": "shtabriz@microsoft.com",
    "family_name": "Tabrizi",
    "given_name": "Shawn",
    "idp": "https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/",
    "in_corp": "true",
    "ipaddr": "XX.XXX.XXX.XXX",
    "name": "Shawn Tabrizi",
    "oid": "41bdce9b-3940-40a9-b2f2-03a003ad599c",
    "platf": "3",
    "scp": "user_impersonation",
    "sub": "hfS9IZ_..._JW8c5Gg",
    "tid": "4a4d599f-e69d-4cd8-a9e1-9882ea340fb5",
    "unique_name": "shtabriz@microsoft.com",
    "ver": "1.0"
}

Payload 2: Client = Web API 2, Resource = Web API 1

{
    "aud": "https://shawntest.onmicrosoft.com/WebApi1",
    "iss": "https://sts.windows.net/4a4d599f-e69d-4cd8-a9e1-9882ea340fb5/",
    "iat": 1500243330,
    "nbf": 1500243330,
    "exp": 1500247230,
    "acr": "1",
    "aio": "ATQAy/...BLDunA",
    "amr": [ "rsa", "mfa" ],
    "appid": "554e427d-36c3-4a77-89a5-a082ee333e12",
    "appidacr": "1",
    "email": "shtabriz@microsoft.com",
    "family_name": "Tabrizi",
    "given_name": "Shawn",
    "groups": [ "0f4374e6-8131-413e-b32b-f98bfdb371ed" ],
    "idp": "https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/",
    "in_corp": "true",
    "ipaddr": "XX.XXX.XXX.XXX",
    "name": "Shawn Tabrizi",
    "oid": "41bdce9b-3940-40a9-b2f2-03a003ad599c",
    "platf": "3",
    "scp": "user_impersonation",
    "sub": "xy..._zGJEZnIB4",
    "tid": "4a4d599f-e69d-4cd8-a9e1-9882ea340fb5",
    "unique_name": "shtabriz@microsoft.com",
    "ver": "1.0",
    "wids": [ "62e90394-69f5-4237-9190-012177145e10" ]
}
Note that we only get the group membership claims when the resource application has this setting, not the client application. The client application has no power to control the types of claims in the token, because ultimately the token is not for them!
If you are building a client application using Azure Active Directory, please do not use the access token to try and get information about the authenticated session. The correct practice here is to request separately an ID Token , or to call the AAD / Microsoft Graph API to get the information you need. I hope you learned exactly how to use the “groupMembershipClaims” property and I hope this helps you build better apps in the future!

Does Company ‘X’ have an Azure Active Directory Tenant?

One of the cool things about the Open ID Configuration endpoint is that it not only tells us random facts about the tenant, but it confirms that the tenant exists! Make sure to check out my last post to learn more about this. Using some clever scripting and this endpoint behavior, we could probably figure out which companies have an Azure Active Directory Tenant. Let’s try that!

$csv = Import-Csv -Path .\input.csv
$output = @()

foreach ($line in $csv)
{
    $companyname = $line.CompanyName
    $companynameencoded = [System.Net.WebUtility]::UrlEncode($companyname)

    $GoogleURI = 'https://www.google.com/search?q=' + $companynameencoded + '&amp;btnI'
 
    try { 
        $GoogleResult = Invoke-WebRequest -Uri $GoogleURI
        $CompanyURI = ([System.Uri]$GoogleResult.BaseResponse.ResponseUri).Host.split('.')[-2..-1] -join '.'
    } catch {
        write-host $_.Exception
        $CompanyURI = "error"
    }

    $OpenIDConfigURL = 'https://login.microsoftonline.com/' + $CompanyURI + '/.well-known/openid-configuration'

    try {
        $OpenIDResult = (Invoke-WebRequest -Uri $OpenIDConfigURL).StatusCode
    } catch {
        $OpenIDResult = $_.Exception.Response.StatusCode.value__
    }

    if ($OpenIDResult -eq 200) {
        $tenant = $true
    } else {
        $tenant = $false
    }

    $result = [pscustomobject]@{
        CompanyName = $companyname.ToString()
        HomepageURI = $CompanyURI.ToString()
        OpenIDResult = $OpenIDResult.ToString()
        HasTenant = $tenant.ToString()
    }

    Write-Host $result
    $output += $result 
}

$output | Export-Csv -Path output.csv -NoTypeInformation

So in summary what does this script do?

We take a CSV which lists a bunch of Company Names. We then do a Google search, and go to the first result (‘I’m Feeling Lucky’). We assume the first result is the homepage of that company, and the domain they would use for their tenant. We pull out the host name, and then check it against the Open ID Configuration endpoint. If we get a valid response from the endpoint, then we say that they have a tenant! Otherwise, we say they do not have a tenant.

One thing to note about these results is that when we get a result that says the company has a tenant, we are nearly 100% correct in that fact. However, if we say that a company does not have a tenant, we are not necessarily correct. It is possible that the google result did not point to their actual domain name, or they are using a different domain name for their AAD Tenant. If you wanted to do this really robustly, you would probably want to get a better source for your domain names than automated google search results. You might want to also look at other combinations like “<companyname>.onmicrosoft.com”, however we are doing just rough estimates here.

So lets look at the result for the Fortune 500. A quick Google search later, and I have a CSV with a list of all the Company Names for all 500 companies. Running it through this script, I find that 417, or 83.4% of companies have AAD, which is just a little off from Microsoft’s public claim of 85%. Not bad for a quick and dirty script!

Secret APIs in Azure Active Directory and Azure Resource Manager

Have you ever wondered what the Tenant ID for Microsoft (microsoft.com) or any other domain is? Have you ever wondered how you can find the right Tenant ID to sign in a user given their Azure Subscription ID?

Oh, you haven’t? Well that is certainly more reasonable than the fact that I have; but, if for some reason you are asking the same questions as I am, let me tell you about some of the “secret APIs” that are available to answer those questions.

Getting the Tenant ID for a Verified Domain in Azure Active Directory

Azure Active Directory tenants have a special type of domain called a ‘verified domain’. Verified domains are what they sound like, domains which a user has proven they own through DNS verification. These domains are unique across all tenants, and can act as a alternative domain to the initial domain given to all tenants (*.onmicrosoft.com).

While authentication and even the AAD Graph API both support the use of these domains for referencing a tenant, not all APIs support this. Sometimes you might need to convert the tenant domain to a Tenant ID… but how?

Well known open id config

Check out the specification here. This Open ID configuration endpoint is required for all Open ID Providers, which AAD is one of. Let’s take a look at what the response looks like for the Microsoft tenant using the verified domain ‘microsoft.com’:

https://login.microsoftonline.com/microsoft.com/.well-known/openid-configuration

{"authorization_endpoint":"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/authorize","token_endpoint":"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt"],"jwks_uri":"https://login.microsoftonline.com/common/discovery/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/logout","response_types_supported":["code","id_token","code id_token","token id_token","token"],"scopes_supported":["openid"],"issuer":"https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/","claims_supported":["sub","iss","cloud_instance_name","cloud_graph_host_name","aud","exp","iat","auth_time","acr","amr","nonce","email","given_name","family_name","nickname"],"microsoft_multi_refresh_token":true,"check_session_iframe":"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/checksession","userinfo_endpoint":"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/openid/userinfo","tenant_region_scope":"WW","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net"}

I honestly have never used most of the data in this JSON, and I am not really sure where it gets used… BUT, you will notice that all of the various authentication endpoints now have a Tenant ID GUID rather than a domain name! This tells us two things:

  1. The Tenant ID for Microsoft.com is 72f988bf-86f1-41af-91ab-2d7cd011db47
  2. (maybe this is obvious already…. but) Microsoft has a tenant!

Now the second realization is kind of a super-set of the first, but it makes me think about something else cool we can do. What if we wanted to get a count and see which companies have an Azure Active Directory Tenant? As long as we know their Domain Name, we should be able to use this endpoint to confirm if a tenant exists! I will save this exploration for my next blog post.

Get the Tenant ID for a Specific Azure Subscription ID

The world of Azure Subscriptions is one of the most complicated spaces that shouldn’t be complicated. Depending on how you start using Azure, you may never even know that you have an Azure Active Directory Tenant. You just have your Live ID, which you use to sign on to the Azure Portal, and from there you can access your Subscription ID!  You can’t even use the ‘common’ endpoint with Live IDs on AAD V1, so your lack of knowledge can be really painful here for app developers. We need your Tenant ID to know the right login endpoint to send you to. Luckily, we can find that using helpful error messages from Azure Resource Manager! All we need is an application for which we can get a token to Azure Resource Manager in the

We can easily execute this plan using my PowerShell Scripts. Update the scripts to have the following configuration:

  • Pick any Tenant ID and Application Information relative to that tenant
  • Set Resource ID to “https://management.azure.com/”
  • Create a variable “$subscriptionId” and set it to the Azure Subscription ID you are looking to investigate.
  • Set up the REST call like this:
try {
    Invoke-RestMethod -Method Get -Uri ("{0}/subscriptions/{1}?api-version=2016-06-01" -f $resourceId, $subscriptionId) -Headers $headers
} catch {
    Write-Host $_.ErrorDetails.Message
}

Hmm… why would I be catching an error? Well let’s run it and see what gets outputted:

{"error":{"code":"InvalidAuthenticationTokenTenant","message":"The access token is from the wrong issuer 'https://sts.windows.net/4a4d599f-e69d-4cd8-a9e1-9882ea340fb5/'. It must match the tenant 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/' associated with this subscription. Please use the authority (URL) 'https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47' to get the token. Note, if the subscription is transferred to another tenant there is no impact to the services, but information about new tenant could take time to propagate (up to an hour). If you just transferred your subscription and see this error message, please try back later."}}

Right in the error they tell us the correct tenant for this Subscription ID!

Please use the authority (URL) ‘https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47

This really is a “secret API”, and we can use it to consistently get back the right tenant for a user, as long as they know what their Azure Subscription is.

Azure AD Authentication with PowerShell and ADAL

In the 3 years I spent on the Azure AD team, I learned a number of useful ‘tricks’ to make my job (and usually the jobs of others) a ton easier. However, if I had to pick just one trick to share to others trying to learn, it would probably be the PowerShell scripts I wrote to quickly get an access token to Azure Active Directory and then call AAD protected APIs like the AAD Graph API.

In general, authentication is hard, and requires way more set up than should be needed for simple testing. To get AAD authentication working on other platforms, you may need to write a ton of code, compile it, or even publish it to the web. With these scripts, you can get authentication and REST API calls done with as little as 13 lines of PowerShell. Running the code is instant, and modifying the REST calls or even the authentication parameters takes seconds rather than minutes.

How to get the samples

You can find all the basic scripts I have written on GitHub here:

https://github.com/shawntabrizi/Azure-AD-Authentication-with-PowerShell-and-ADAL

I provide different scripts for different authentication flows:

  1. Authorization Code Grant Flow for Confidential Client
  2. Native Client Authentication
  3. Client Credential Flow
    1. Using Application Key
    2. Using Client Certificate

Each script ends with a REST API call to get the list of Users in your tenant using the AAD Graph API. You should be able to do this with any application because it uses the “Sign in and read basic profile” permission which is assigned to all AAD Applications by default.

Note that to get these samples running, you will need to add the .NET dlls for ADAL v2 into the ADAL folder. You can find those files on NuGet.

Why it is so darn useful

So now that you have the scripts downloaded, and hopefully working, let me illustrate to you just a few of the different scenarios where I have used this tool to greatly simplify my work.

Verifying Token Claims

So many errors in AAD app development come from some sort of wrong setting, which may manifest itself in your access token. You might want to check the ‘scp’ claims to see if your app has the right permissions. You might want to check the ‘tid’ claim to make sure that you are getting a token to the right tenant! Or even the ‘aud’ claim to make sure the token is for the correct resource. You can simply pump in the settings for your application into the appropriate PowerShell script, run the script, and you will get a .txt file with your access token in it. Then you can pop that JWT token into a JWT decoder like the one I created… and viola! There are your claims, and it took literally seconds.

Making quick REST API calls

Another thing that comes up very often around work is just pulling random data from AAD. Let’s say that someone wants to know the settings of a certain Application Object, Service Principal, or even User. You may be able to do this with tools like the Graph Explorer, but what about some more complicated queries, or ones that you want to download to a file for later? Or how about simply wanting to test that YOUR app can make those queries rather than the Graph Explorer app. Not to mention the fact that you can call ANY AAD protected API, not just the AAD Graph API with these scripts. Simply update the Invoke-RestMethod command and bam, results will be saved into a .json file!

Making scripted REST API calls

Maybe you are still not convinced that these scripts are useful. Most of what I showed above can be done if you want to use multiple other tools. However, I challenge you to find a quicker way to create “scripted” REST API calls. What do I mean by that? Lets say you wanted to pull a list of all the users in your company. Well the AAD Graph API can return at most 999 results in a single call, so you probably want to create a loop that iterates over the paged results that the Graph API returns. This is SIMPLE!

Here is the loop I wrote to solve this exact problem:

$result = Invoke-RestMethod -Method Get -Uri ('{0}/{1}/users/?api-version=1.6&amp;amp;amp;$top=999' -f $resourceId,$tenantId) -Headers $headers
$count = 0
$result.value | Export-Csv ([String]$count + "_" +$output) -Encoding UTF8

while (($result.'odata.nextLink' -split 'skiptoken=')[1] -ne $null)
{
  $skiptoken = ($result.'odata.nextLink' -split 'skiptoken=')[1]
  Write-Host ('{0}/{1}/users/?api-version=1.6&amp;amp;amp;$top=999&amp;amp;amp;$skiptoken={2}' -f $resourceId,$tenantId,$skiptoken)

  try
  {
    $result = Invoke-RestMethod -Method Get -Uri ('{0}/{1}/users/?api-version=1.6&amp;amp;amp;$top=999&amp;amp;amp;$skiptoken={2}' -f $resourceId,$tenantId,$skiptoken) -Headers $headers
    $count += 1
    $result.value | Export-Csv ([String]$count + "_" + $output) -Encoding UTF8
  }
  catch
  {
    Write-Host "Error with Invoke Rest Method!"
    Write-Host $result.'odata.nextLink'
    break
  }
}

The result is a folder of CSV files all numbered and ready to be merged. If the script fails at some point (like if I lose an internet connection), I can use the outputted ‘odata.nextLink’ and just pick up where I left off. I couldn’t imagine doing this any other way for my needs.

Convinced?

I hope that you too will be able to find this little tool helpful for your day to day needs. Let me know if you find some other unconventional uses for this!

Decoding JWT Tokens

Forewarning: I know that “JWT Tokens” is case of RAS syndrome… but I can’t help it!

Are your tokens safe when using online decoders?

In the identity space, decoding JSON Web Tokens (JWT tokens) is a regular event. One of the first things we do in order to try and debug issues that customers or partners are having is taking a quick peek into the access tokens they are using, and seeing if anything is wrong.

In Azure Active Directory, we are commonly looking at the “audience” claim or the “scopes” in the token to make sure that they have the token to the right resource, and they have the right level of permissions for the task. But sometimes problems can be even more subtle than that. For example, the “tenant” information can be wrong, and people may never notice the subtle difference in GUID.

Either way, being able to read the contents of a token is crucial, and so I have always relied on small web apps created by others to do this. However, at work recently, there was discussion about how the most popular site for this (https://jwt.io/) may be storing the tokens that are submitted into the app. If someone submits a token that is still active, there is a possibility that the site could use that token and impersonate you! Furthermore, the website was created by a Microsoft competitor, Auth0… so just bad news in general.

I wanted to create my own JWT decoder so that I know for certain that my tokens are not being used maliciously, and so I could learn a little more about JWT tokens in general.

I created this very basic page: http://shawntabrizi.com/jwt/

You can find the GitHub source here. Let’s talk about what I did.

JSON Web Token Structure

A JWT token is broken up into 3 sections, all separated by periods. The first section is the Header, which contains information about the token type and the algorithm used to sign or encrypt that token. The second section is the Payload, where all the main claims are stored for the token. Finally, the third section is the token signature, where a token issuer can prove that they were the ones that actually minted the token. Tokens do not need to be signed, and if they are not, the third section will be empty. However, they will still contain a period to separate it from the second section as shown here.

The problem I needed to solve was pretty simple: Take the encoded JWT token, and get the claims out of it. I think the easiest way to explain the steps is simply to look at my commented code:

//This function takes a base 64 url encoded string, and converts it to a JSON object... using a few steps.
function decoder(base64url) {
    try {
        //Convert base 64 url to base 64
        var base64 = base64url.replace('-', '+').replace('_', '/')
        //atob() is a built in JS function that decodes a base-64 encoded string
        var utf8 = atob(base64)
        //Then parse that into JSON
        var json = JSON.parse(utf8)
        //Then make that JSON look pretty
        var json_string = JSON.stringify(json, null, 4)
    } catch (err) {
        json_string = "Bad Section.\nError: " + err.message
    }
    return json_string
}

JWT tokens are Base 64 URL encoded. While they are nearly the same, characters like “+” and “/” turn into “-” and “_” respectively. Learn more here. From there, converting a Base 64 encoded string to a pretty JSON string is really self explanatory.

The rest of the work beyond this is just handling random user inputs. We have checks to verify the individual parts of the token are good, and whether or not the token contains a signature. As I suspected, creating a site to decode JWT tokens is really quite simple, and now I have my own site to do it on!