We talked about this in our last community hours. Check out the video above!
If you’ve ever worked with the Microsoft identity platform (aka Azure AD, aka Azure AD B2C), there is a good chance that you have had to work with scopes, including the
/.default scope. In this blog post, we’re going to cover some of the basics and explain what the
/.default scope is, when to use it and why.
When we need to connect to APIs or services secured with OAuth2 (called resources in openid and oauth parlance), such as the Microsoft Graph API, Azure APIs, third-party APIs or our own APIs, we need to request an access token for that resource. For Microsoft services, the vast majority are secured with Azure AD, meaning you'll need to get tokens from Azure AD in order to use those services from your applications.
Sometimes OAuth2 is referred to as 'Bearer Authentication' - this is due to how we use the token with a resource: it's put into the
AuthorizationHTTP header with a value of
Bearer <access_token value>
When users sign-in to your app, Azure AD sends you back an
id_token, which proves their identity to your application – but when your application also needs to connect to a resource, we need an access token specifically for that resource. In short, the
id_tokenis for your application to use, while the
access_token is for you to send to other resources (APIs) your app is using.
Scopes can be thought of as permissions within a resource – for example, Microsoft Graph exposes Office 365 scopes like
Mail.Read (read the user’s mail) or
Files.Read (read the user’s OneDrive files). In this case, the resource is
https://graph.microsoft.com while the permission is
Mail.Read. Putting these together gives us our full scope:
Once we know our scope, we need to ask the user if it's ok to use that scope. This is called consent. If the user consents to
https://graph.microsoft.com/Mail.Read, our application will get an access token it can use to read a user’s mailbox.
You can think of it like permissions on your phone – when you install a new app, most smartphones will say "This app would like to use your location. Is that OK?"
What makes scopes so useful in building user trust is that our users can allow other apps to access their data without having to give those other apps full access to everything. In my phone example, if I grant app ABC access to my location, that’s all the app receives – it can’t also read my contacts, for example, unless the app asks and the user allows that to happen.
Imagine we’ve built an app that handles ordering business cards.
It would be really helpful to pre-populate the user’s info in the card, so we request the
User.Read permission from Microsoft Graph. This includes profile information like their display name, email address, etc.
If you're keeping score at home, that means we'll need this scope:
But we also want to give them an option to save their design, or upload a custom design. We implement a normal file picker so a user can upload from their PC, but wouldn’t it be cool to offer upload and download from OneDrive or a SharePoint site? To do that, we will need the
Now our scope list is:
Remember, however - not all users will want to do this. Some users won't have a OneDrive or Sharepoint site, other users may have all of their files locally.
Sample of users actually using these permissions:
https://graph.microsoft.com/User.Read: this is for signing into the app - 100% of users will use this
https://graph.microsoft.com/Files.ReadWrite: this is for accessing OneDrive - say, 40% of users will do this
https://graph.microsoft.com/Sites.ReadWrite.All: this is for accessing a SharePoint site - we'll estimate 10% of users want to do this
In Azure AD v1 (which you may also hear being referred to as ‘the v1 endpoint’), we had to add all the permissions we needed upfront - configured on the App Registration in Azure AD. This meant users would also need to allow those permissions upfront, just to sign-in to our app. This is called ‘static consent’. Let's see what that looks like:
Meanwhile, this is what your users will see when signing into the app:
Whoa! That's a lot of permissions. I just wanted to order some business cards, but now I'm being asked to let this app I've never used write to my files? Access sharepoint sites? No way, I'm outta here - I'll go order business cards somewhere else.
If the user does allow those permissions, it means your application is going to have permission to read a user’s files, even if you never need to read their files! Not particularly optimal for our app or our users.
The Azure AD v2 (aka Microsoft identity platform, aka ‘the v2 endpoint’) scope & permission system fixes this, by allowing dynamic consent – instead of requiring the developers to declare all permissions upfront, v2 allows developers to ask at any time. In our business card example, this means a user is only asked to consent to their profile data being read on sign-in, while users who elect to 'upload a custom design from OneDrive' (by clicking a button in your app, etc) will be prompted for the
Files.Read permission when they perform the action. Better for our users, better for us as the developer 😊
Now that we’ve written two pages of what got us here, let’s get to the point of this post – the
/.default scope. The short description is the
/.default scope is a shortcut back to the Azure AD v1 behavior (e.g., static consent). When we request a
/.default scope (for example,
https: //graph.microsoft.com/.default), our users will be asked to consent to all of the configured permissions present on our Azure AD App Registration for that specific resource (e.g., https: //graph.microsoft.com).
In our business card example, this means that a user would be prompted to consent to all permissions we’ve configured upfront.
For migrating old apps that currently use Azure AD v1 (including apps that use ADAL, the Active Directory Authentication Library), the
/.default scope offers an easier migration path, as the developer gets the same behavior from the v2 endpoint keeping the experience consistent.
There are two extra scenarios where the
/.default scope is required:
client_credentials, where our app is making service-to-service calls or using application-only permissions (also known as application app roles in Azure AD parlance), or
when using the
on-behalf-of(OBO) flow, where our API is making calls on behalf of the user to a different API; something like this: client app --> our API --> Graph API.
In both scenarios above, there is no user interface and no user interaction. In the
client_credentials case, an application is using its own identity (not that of a user), so there is no concept of dynamic consent, as the application must statically configure the permissions that it needs - who would it ask for extra permission at runtime if there is no user present?
on-behalf-of (OBO) is similar; since the backend API is making a request to another API, there is no user interface for asking for additional permissions, so permissions must be set statically between APIs. This means that your scopes in token requests for service-to-service and on-behalf-of flows must use a scope of
your-app-id-uri/.default – e.g., https: //graph.microsoft.com/.default, or https: //your-app.your-co.com/.default.
You can read up more on the official doc about /.default, scopes & consent here: Microsoft identity platform scopes, permissions, and consent.
Check us out live Tuesdays & Thursdays at 10a ET, 7a PT at https://aka.ms/425show!