Azure / Azure/azure-functions-host

`ClaimsPrincipal.IsInRole` doesn't work with AAD application roles

Open
#3,898 10 comments 5 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
2k
Forks
482
Avg merge
2d 12h
Merged PRs (30d)
38

Description

When using the EasyAuth `ClaimsPrincipal` binding with an Azure AD token with an application role, the `ClaimsPrincipal.IsInRole()` method always returns false even when the token has the role included.

#### Investigative information

- Timestamp: From December 4 until now (December 17), 2018
- Function App version (1.0 or 2.0): 2.0
- Function App name: applies across multiple (every one I've tried)
- Function name(s) (as appropriate): multiple
- Invocation ID: N/A
- Region: multiple; I've tested centralus and australiasoutheast

#### Repro steps

1. Create a new function app. Enable EasyAuth through the portal.
2. Update the app registration in Azure AD to add a new app role to the app's manifest, such as:
```json
{
"allowedMemberTypes": [
"Application"
],
"description": "Administrators can manage the Surveys in their tenant",
"displayName": "SurveyAdmin",
"id": "c20e145e-5459-4a6c-a074-b942bbd4cfe1",
"isEnabled": true,
"value": "SurveyAdmin"
}
```
3. Create a client app in the same AAD tenant, create a key, assign the role from step 2 to the client app, and grant the permission.
4. Create a function in the portal with the following code:

```csharp
#r "Newtonsoft.Json"

using System.Security.Claims;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

public static async Task Run(HttpRequest req, ClaimsPrincipal principal, ILogger log)
{
log.LogInformation($"Found {principal.Identities.Count()} identities.");
foreach (var identity in principal.Identities)
{
log.LogInformation($"Identity {identity.Name}:");
log.LogInformation($"Auth type is {identity.AuthenticationType}");
foreach (var claim in identity.Claims)
{
log.LogInformation($"Claim '{claim.Type}' = '{claim.Value}'");
}
}

var isInRole = principal.IsInRole("SurveyAdmin");
log.LogInformation($"Principal is {(isInRole ? "" : "NOT")} in the SurveyAdmin role.");

return new OkResult();
}
```

5. Obtain an Azure AD token. Observe that the token includes the `roles` claim, which is a string array with a single item (`SurveyAdmin`).
6. Send a request to the function, attaching the token. Observe that the role claim is included in the enumerated list of claims logged out, but the log also includes `Principal is NOT in the SurveryAdmin role.` since the `IsInRole` method has returned `false`.

#### Expected behavior

The `roles` claim contains the roles associated with that principal. It should be honoured when evaluating the `IsInRole` method. (Also, on the main EasyAuth issue, @ConnorMcMahon [indicated](https://github.com/Azure/azure-functions-host/issues/33#issuecomment-441811552) that this specific scenario should work.)

#### Actual behavior

The `IsInRole` method returns `false`.

#### Known workarounds

By manually inspecting the claims associated with the identity, we can perform our own version of the `IsInRole` logic.

#### Related information

Contributor guide

Open the contributing guide

Research direction

Start with the EasyAuth ClaimsPrincipal binding and reproduce the behavior using the C# function code and Azure AD token steps in the issue. Inspect how the roles claim is mapped during binding and compare it with ClaimsPrincipal.IsInRole. Done means the provided SurveyAdmin repro returns true while preserving the existing claims behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp
Domain
authentication, authorization
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.