1
votes

We have an AAD application that only required MS Graph API permissions until recently. Now it requires both MS Graph API and AAD API permissions. The application has a process to determine when the user needs to re-consent. Prior to the addition of the AAD API permission, the re-consent process would determine if re-consent was necessary by parsing the MS Graph API access token to obtain it’s scopes. It would then compare those scopes against a static list contained within the application. If any of the required permissions where not part of the access token’s scopes the user was redirected to the consent page to re-consent. With the addition of the AAD API permissions the application requests and AAD API access token using the appropriate AAD resource. However, it seems the scopes associated both APIs are contained within both the MS Graph access token and the AAD access token.

Code to get the access tokens:

Notifications = new OpenIdConnectAuthenticationNotifications()
{
    AuthorizationCodeReceived = async (context) =>
    {
        var code = context.Code;

        // retriever caller data from the incoming principal
        string claimClientId = Claim.Value(context.AuthenticationTicket.Identity.Claims, Claim.Type.ClientId);

        string upn = Claim.Value(context.AuthenticationTicket.Identity.Claims, ClaimTypes.Name);
        string officeTenantId = Claim.Value(context.AuthenticationTicket.Identity.Claims, Claim.Type.OfficeTenantId, upn);

        //Obtain MS Graph API Access Token
        Credential = new ClientCredential(ClientId, AppKey);
        var signedInUserId = Claim.Value(context.AuthenticationTicket.Identity.Claims, ClaimTypes.NameIdentifier);
        AuthenticationContext authContext = new AuthenticationContext($"{LoginEndpoint}/{officeTenantId}", new RedisTokenCache(signedInUserId));
        AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), Credential, GraphResourceId);
        //Obtain AAD Graph API Access Token
        AuthenticationResult aadResult = await authContext.AcquireTokenByAuthorizationCodeAsync(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), Credential, AadGraphResourceId);
…

Scopes I’m getting back:

  • Resource = graph.windows.net, Scopes = Directory.AccessAsUser.All Directory.ReadWrite.All Files.ReadWrite.All,Group.ReadWrite.All,profile,User.Read
  • Resource = graph.microsoft.com, Scopes = Directory.AccessAsUser.All,Directory.ReadWrite.All,Files.ReadWrite.All,Group.ReadWrite.All,profile,User.Read

Scopes I’m expecting:

  • Resource = graph.windows.net, Scopes = Directory.AccessAsUser.All,User.Read
  • Resource = graph.microsoft.com, Scopes = Directory.ReadWrite.All,Files.ReadWrite.All,Group.ReadWrite.All,profile,User.Read

Based on what I'm seeing it looks like both resources get the scopes associated with all the APIs the user consented to. Is this expected behavior? How do I obtain separate lists of scopes for each resource?

Here's some output from MS Graph Explorer, AAD Graph Explorer and JWT decoder:

//Service Principal for my test app
{
    "odata.metadata": "https://graph.windows.net/myorganization/$metadata#directoryObjects/Microsoft.DirectoryServices.ServicePrincipal/@Element",
    "odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
    "objectType": "ServicePrincipal",
    "objectId": "2f9f4cf5-7576-42c9-8f9d-c1c3e6e63b4d",
    "appDisplayName": "My Test App",
}

//Service Principal for MS Graph API
{
    "odata.metadata": "https://graph.windows.net/myorganization/$metadata#directoryObjects/Microsoft.DirectoryServices.ServicePrincipal/@Element",
    "odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
    "objectType": "ServicePrincipal",
    "objectId": "4036b7c7-c9b0-447f-a5d8-18794d0d2a23",
    "appDisplayName": "Microsoft Graph",
    "appId": "00000003-0000-0000-c000-000000000000",
}

//Service Principal for AAD API
{
    "odata.metadata": "https://graph.windows.net/myorganization/$metadata#directoryObjects/Microsoft.DirectoryServices.ServicePrincipal/@Element",
    "odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
    "objectType": "ServicePrincipal",
    "objectId": "aeb2d60b-0681-40f5-abe9-e0e66e793f3e",
    "appDisplayName": "Windows Azure Active Directory",
    "appId": "00000002-0000-0000-c000-000000000000",
}

//oAuth2PermissionGrants for user
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#oauth2PermissionGrants",
    "value": [
        {
            "clientId": "2f9f4cf5-7576-42c9-8f9d-c1c3e6e63b4d",
            "consentType": "Principal",
            "expiryTime": "2018-03-30T21:31:15.7114922Z",
            "id": "9UyfL3Z1yUKPncHD5uY7TQvWsq6BBvVAq-ng5m55Pz6rJZQ8BUBCT5nGOphIZSeR",
            "principalId": "3c9425ab-4005-4f42-99c6-3a9848652791",
            "resourceId": "aeb2d60b-0681-40f5-abe9-e0e66e793f3e",
            "scope": "User.Read Directory.AccessAsUser.All",
            "startTime": "0001-01-01T00:00:00Z"
        },
        {
            "clientId": "2f9f4cf5-7576-42c9-8f9d-c1c3e6e63b4d",
            "consentType": "Principal",
            "expiryTime": "2018-03-30T21:31:15.7114922Z",
            "id": "9UyfL3Z1yUKPncHD5uY7Tce3NkCwyX9EpdgYeU0NKiOrJZQ8BUBCT5nGOphIZSeR",
            "principalId": "3c9425ab-4005-4f42-99c6-3a9848652791",
            "resourceId": "4036b7c7-c9b0-447f-a5d8-18794d0d2a23",
            "scope": "profile Files.ReadWrite.All Directory.ReadWrite.All Group.ReadWrite.All User.Read",
            "startTime": "0001-01-01T00:00:00Z"
        }
    ]
}

//Access tokens granted to user after sign-in
{
    "aud": "https://graph.windows.net",
    "scp": "Directory.AccessAsUser.All Directory.ReadWrite.All Files.ReadWrite.All Group.ReadWrite.All profile User.Read",
}
{
    "aud": "https://graph.microsoft.com",
    "scp": "Directory.AccessAsUser.All Directory.ReadWrite.All Files.ReadWrite.All Group.ReadWrite.All profile User.Read",
}
1

1 Answers

0
votes

When you request your scopes you'll want to prefix the scopes with the resource identifier.

For Azure AD Graph API this is https://graph.windows.net/ and for Microsoft Graph API it is https://graph.microsoft.com/. You complete scope request should look something like

&scope=https%3A%2F%2Fgraph.windows.net%2FDirectory.AccessAsUser.All+https%3A%2F%2Fgraph.windows.net%2FUser.Read+https%3A%2F%2Fgraph.microsoft.com%2FDirectory.ReadWrite.All+https%3A%2F%2Fgraph.microsoft.com%2FFiles.ReadWrite.All+https%3A%2F%2Fgraph.microsoft.com%2FGroup.ReadWrite.All+https%3A%2F%2Fgraph.microsoft.com%2Fprofile+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read