Skip to content

Commit a784211

Browse files
committed
adressing PR feedback
1 parent 512970a commit a784211

File tree

2 files changed

+51
-32
lines changed

2 files changed

+51
-32
lines changed

articles/active-directory/develop/scenario-protected-web-api-app-registration.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ Scopes are usually of the form `resourceURI/scopeName`. For Microsoft Graph, the
5656

5757
During app registration, you'll need to define the following parameters:
5858

59-
- One resource URI - By default the application registration portal recommends that you to use `api://{clientId}`. This resource URI is unique, but it's not human readable. You can change it, but make sure that it's unique.
60-
- One or several **scopes** (that client applications will refer to as **delegated permissions** for your Web API)
61-
- One or several **app roles** (that client applications will refer to as **application permissions** for your Web API)
59+
- The resource URI - By default the application registration portal recommends that you to use `api://{clientId}`. This resource URI is unique, but it's not human readable. You can change it, but make sure that it's unique.
60+
- One or more **scopes** (to client applications, they will show up as **delegated permissions** for your Web API)
61+
- One or more **app roles** (to client applications, they will show up as **application permissions** for your Web API)
6262

6363
The scopes are also displayed on the consent screen that's presented to end users who use your application. Therefore, you'll need to provide the corresponding strings that describe the scope:
6464

@@ -84,15 +84,15 @@ The scopes are also displayed on the consent screen that's presented to end user
8484

8585
In this paragraph, you'll learn how to register your protected Web API so that it can be called securely by daemon applications:
8686

87-
- you' statuill need to expose application permissions
88-
- tenant admins may require AAD to acquire tokens for your Web App only for registered applications;
87+
- you'll need to expose **application permissions**. You will only declare application permissions as daemon applications do not interact with users and therefore delegated permissions would not make sense.
88+
- tenant admins may require Azure AD to issue tokens for your Web App to only applications that have registered that they want to access one of the Web API apps permissions.
8989

9090
#### How to expose application permissions (app roles)
9191

9292
To Expose application permissions, you'll need to edit the manifest.
9393

9494
1. In the application registration for your application, click **Manifest**.
95-
1. Edit the manifest by locating the `appRoles` setting and adding one or several application roles. The role definition is provided in the JSON block below. Leave the `allowedMemberTypes` to "Application" only.
95+
1. Edit the manifest by locating the `appRoles` setting and adding one or several application roles. The role definition is provided in the sample JSON block below. Leave the `allowedMemberTypes` to "Application" only. Please make sure that the **id** is a unique guid and **displayName** and **Value** don't contain any spaces.
9696
1. Save the manifest.
9797

9898
The content of `appRoles` should be the following (the `id` can be any unique GUID)
@@ -114,7 +114,7 @@ The content of `appRoles` should be the following (the `id` can be any unique GU
114114

115115
#### How to ensure that Azure AD issues tokens for your Web API only to allowed clients
116116

117-
The Web API tests for the app role (that's the developer way of doing it). But you can even ask Azure Active Directory to issue a token for your Web API only to applications that were approved by the tenant admin. To add this additional security:
117+
The Web API checks for the app role (that's the developer way of doing it). But you can even configure Azure Active Directory to issue a token for your Web API only to applications that were approved by the tenant admin to access your API. To add this additional security:
118118

119119
1. On the app **Overview** page for your app registration, select the hyperlink with the name of your application in **Managed application in local directory**. The title for this field can be truncated. You could, for instance, read: `Managed application in ...`
120120

@@ -127,9 +127,9 @@ The Web API tests for the app role (that's the developer way of doing it). But y
127127

128128
> [!IMPORTANT]
129129
>
130-
> By setting **User assignment required?** to **Yes**, AAD will check the app role assignments of the clients when they request an access token for the Web API. If the client was not be assigned to any AppRoles, AAD would just return `invalid_client: AADSTS501051: Application xxxx is not assigned to a role for the xxxx`
130+
> By setting **User assignment required?** to **Yes**, AAD will check the app role assignments of the clients when they request an access token for the Web API. If the client was not be assigned to any AppRoles, AAD would just return the following error: `invalid_client: AADSTS501051: Application xxxx is not assigned to a role for the xxxx`
131131
>
132-
> If you keep **User assignment required?** to **No**, <span style='background-color:yellow; display:inline'>Azure AD won’t check the app role assignments when a client requests an access token to your Web API</span>. Therefore, any daemon client (that is any client using client credentials flow) would still be able to obtain the access token for the Web API just by specifying its audience. Any application, would be able to access the API without having to request permissions for it. Now this is not then end of it, as your Web API can always, as is done in this sample, verify that the application has the right role (which was authorized by the tenant admin), by validating that the access token has a `roles` claim, and the right value for this claim (in our case `access_as_application`).
132+
> If you keep **User assignment required?** to **No**, <span style='background-color:yellow; display:inline'>Azure AD won’t check the app role assignments when a client requests an access token for your Web API</span>. Therefore, any daemon client (that is any client using client credentials flow) would still be able to obtain an access token for the API just by specifying its audience. Any application, would be able to access the API without having to request permissions for it. Now, this is not then end of it, as your Web API can always, as explained in the next section, verify that the application has the right role (which was authorized by the tenant admin), by validating that the access token has a `roles` claim, and the right value for this claim (in our case `access_as_application`).
133133
134134
1. Select **Save**
135135

articles/active-directory/develop/scenario-protected-web-api-verification-scope-app-roles.md

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ms.workload: identity
1616
ms.date: 05/07/2019
1717
ms.author: jmprieur
1818
ms.custom: aaddev
19-
#Customer intent: As an application developer, I want to know how to write a protected Web API using the Microsoft identity platform for developers.
19+
#Customer intent: As an application developer, I want to learn how to write a protected Web API using the Microsoft identity platform for developers.
2020
ms.collection: M365-identity-device-management
2121
---
2222

@@ -30,7 +30,7 @@ This article describes how you can add authorization to your Web API. This prote
3030
For an ASP.NET / ASP.NET Core Web API to be protected, you'll need to add the `[Authorize]` attribute on:
3131

3232
- the Controller itself if you want all the actions of the controller to be protected
33-
- the individual controller action for your API, otherwise.
33+
- or the individual controller action for your API.
3434

3535
```CSharp
3636
[Authorize]
@@ -73,14 +73,14 @@ public class TodoListController : Controller
7373

7474
The `VerifyUserHasAnyAcceptedScope` method would do something like the following:
7575

76-
- verify that there's a claims named `scope`
77-
- verify that the claim has a value containing the scope expected by the API.
76+
- verify that there's a claims named `http://schemas.microsoft.com/identity/claims/scope` or `scp`
77+
- verify that the claim has a value containing the scope expected by the API.
7878

7979
```CSharp
8080
/// <summary>
8181
/// When applied to an <see cref="HttpContext"/>, verifies that the user authenticated in the
82-
/// Web API has any of the accepted scopes. *
83-
/// If the authentication user does not have any of these <paramref name="acceptedScopes"/>, the
82+
/// Web API has any of the accepted scopes.
83+
/// If the authenticated user does not have any of these <paramref name="acceptedScopes"/>, the
8484
/// method throws an HTTP Unauthorized with the message telling which scopes are expected in the token
8585
/// </summary>
8686
/// <param name="acceptedScopes">Scopes accepted by this API</param>
@@ -104,15 +104,13 @@ The `VerifyUserHasAnyAcceptedScope` method would do something like the following
104104
}
105105
```
106106

107-
This sample code is for ASP.NET Core. For ASP.NET just replace `HttpContext.User` by `ClaimsPrincipal.Current`, and the claim type `"http://schemas.microsoft.com/identity/claims/scope"` by `"scope"` (See also the code snippet below)
108-
107+
This sample code is for ASP.NET Core. For ASP.NET just replace `HttpContext.User` by `ClaimsPrincipal.Current`, and the claim type `"http://schemas.microsoft.com/identity/claims/scope"` by `"scp"` (See also the code snippet below)
109108

110109
## Verifying app roles in APIs called by daemon apps
111110

112-
If you Web API is called by a [Daemon application](scenario-daemon-overview.md), then that application should require an application permission
113-
to your Web API. We've seen in [scenario-protected-web-api-app-registration.md#how-to-expose-application-permissions--app-roles-] that your API
114-
exposes such permissions (for instance as the `access_as_application` app role). You now need to have your APIs verify that the token it received contains the `roles` claims and
115-
that this claim has the value it expects. To do this, the code is similar to the code that verifies delegated permissions, except that, instead of testing for `scopes`, your controller action will test for `roles`:
111+
If your Web API is called by a [Daemon application](scenario-daemon-overview.md), then that application should require an application permission to your Web API. We've seen in [scenario-protected-web-api-app-registration.md#how-to-expose-application-permissions--app-roles-] that your API exposes such permissions (for instance as the `access_as_application` app role).
112+
You now need to have your APIs verify that the token it received contains the `roles` claims and
113+
that this claim has the value it expects. The code doing this verification is similar to the code that verifies delegated permissions, except that, instead of testing for `scopes`, your controller action will test for `roles`:
116114

117115
```CSharp
118116
[Authorize]
@@ -125,26 +123,47 @@ public class TodoListController : ApiController
125123
}
126124
```
127125

128-
The ValidateAppRole() method can be something like this:
126+
The `ValidateAppRole()` method can be something like this:
129127

130128
```CSharp
131-
private void ValidateAppRole(string appRole)
129+
private void ValidateAppRole(string appRole)
130+
{
131+
//
132+
// The `role` claim tells you what permissions the client application has in the service.
133+
// In this case we look for a `role` value of `access_as_application`
134+
//
135+
136+
if (!isAppOnlyToken)
132137
{
133-
//
134-
// The `role` claim tells you what permissions the client application has in the service.
135-
// In this case we look for a `role` value of `access_as_application`
136-
//
137-
Claim scopeClaim = ClaimsPrincipal.Current.FindFirst("roles");
138-
if (scopeClaim == null || (scopeClaim.Value != appRole))
139-
{
140-
throw new HttpResponseException(new HttpResponseMessage { StatusCode = HttpStatusCode.Unauthorized, ReasonPhrase = $"The 'roles' claim does not contain '{appRole}' or was not found" });
141-
}
142138
}
139+
Claim roleClaim = ClaimsPrincipal.Current.FindFirst("roles");
140+
if (roleClaim == null || !roleClaim.Value.Split(' ').Contains(appRole))
141+
{
142+
throw new HttpResponseException(new HttpResponseMessage
143+
{ StatusCode = HttpStatusCode.Unauthorized,
144+
ReasonPhrase = $"The 'roles' claim does not contain '{appRole}' or was not found"
145+
});
146+
}
147+
}
143148
}
144149
```
145150

146151
This sample code is for ASP.NET. For ASP.NET Core, just replace `ClaimsPrincipal.Current` by `HttpContext.User` and the `"roles"` claim name by `"http://schemas.microsoft.com/identity/claims/roles"` (see also the code snippet above)
147152

153+
### Accepting app only tokens if the Web API should only be called by daemon apps
154+
155+
The `roles` claim is also used for users in user assignment patterns (See [How to: Add app roles in your application and receive them in the token](howto-add-app-roles-in-azure-ad-apps.md)). So just checking roles will allow apps to sign in as users and the other way around.
156+
157+
If you want to only allow daemon applications to call your Web API, you'll want to add a condition, when you validate the app role, that the token is an app-only token:
158+
159+
```CSharp
160+
string oid = ClaimsPrincipal.Current.FindFirst("oid");
161+
string sub = ClaimsPrincipal.Current.FindFirst("sub");
162+
bool isAppOnlyToken = oid == sub;
163+
```
164+
165+
Of course checking the inverse condition will allow only app that sign-in a user to call your API.
166+
148167
## Next steps
149168

150169
> [!div class="nextstepaction"]

0 commit comments

Comments
 (0)