Skip to content

Commit 5ed6654

Browse files
merged
2 parents 6527273 + e923739 commit 5ed6654

File tree

6 files changed

+52
-20
lines changed

6 files changed

+52
-20
lines changed

5-WebApp-AuthZ/5-2-Groups/AppCreationScripts/Configure.ps1

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ Function ConfigureApplications
197197
# Add Required Resources Access (from 'webApp' to 'Microsoft Graph')
198198
Write-Host "Getting access from 'webApp' to 'Microsoft Graph'"
199199
$requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" `
200-
-requiredDelegatedPermissions "User.Read GroupMember.Read.All" `
200+
-requiredDelegatedPermissions "User.Read|GroupMember.Read.All" `
201201

202202
$requiredResourcesAccess.Add($requiredPermissions)
203203

@@ -209,6 +209,15 @@ Function ConfigureApplications
209209
$configFile = $pwd.Path + "\..\appsettings.json"
210210
Write-Host "Updating the sample code ($configFile)"
211211

212+
Write-Host ""
213+
Write-Host -ForegroundColor Green "------------------------------------------------------------------------------------------------"
214+
Write-Host "IMPORTANT: Please follow the instructions below to complete a few manual step(s) in the Azure portal":
215+
Write-Host "- For 'webApp'"
216+
Write-Host " - Navigate to '$webAppPortalUrl'"
217+
Write-Host " - Navigate to the API Permissions page and select 'Grant admin consent for (your tenant)'" -ForegroundColor Red
218+
219+
Write-Host -ForegroundColor Green "------------------------------------------------------------------------------------------------"
220+
212221
Add-Content -Value "</tbody></table></body></html>" -Path createdApps.html
213222
}
214223

5-WebApp-AuthZ/5-2-Groups/AppCreationScripts/sample.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
"RequiredResourcesAccess": [
2626
{
2727
"Resource": "Microsoft Graph",
28-
"DelegatedPermissions": [ "User.Read GroupMember.Read.All" ]
28+
"DelegatedPermissions": [ "User.Read", "GroupMember.Read.All" ]
29+
}
30+
],
31+
"ManualSteps": [
32+
{
33+
"Comment": "Navigate to the API Permissions page and select 'Grant admin consent for (your tenant)'"
2934
}
3035
]
3136
}

5-WebApp-AuthZ/5-2-Groups/Infrastructure/Constants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ public static class Constants
44
{
55
public const string ScopeUserRead = "User.Read";
66

7+
public const string ScopeGroupMemberRead = "GroupMember.Read.All";
8+
79
public const string BearerAuthorizationScheme = "Bearer";
810
}
911
}

5-WebApp-AuthZ/5-2-Groups/Services/MicrosoftGraph-Rest/GraphHelper.cs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,45 @@ public static async Task ProcessClaimsForGroupsOverage(TokenValidatedContext con
2424
// Checks if the incoming token contained a 'Group Overage' claim.
2525
if (context.Principal.Claims.Any(x => x.Type == "hasgroups" || (x.Type == "_claim_names" && x.Value == "{\"groups\":\"src1\"}")))
2626
{
27-
// For this API call to succeed, the app should have permission 'GroupMember.Read.All' granted.
28-
var graph = context.HttpContext.RequestServices.GetService<GraphServiceClient>();
27+
// Before instatntiating GraphServiceClient, the app should have granted admin consent for 'GroupMember.Read.All' permission.
28+
var graphClient = context.HttpContext.RequestServices.GetService<GraphServiceClient>();
2929

30-
if (graph == null)
30+
if (graphClient == null)
3131
{
3232
Console.WriteLine("No service for type 'Microsoft.Graph.GraphServiceClient' has been registered in the Startup.");
3333
}
34+
35+
// Checks if the SecurityToken is not null.
36+
// For the Web App, SecurityToken contains value of the ID Token.
3437
else if (context.SecurityToken != null)
3538
{
36-
// Check if an on-behalf-of all was made to a Web API
39+
// Checks if 'JwtSecurityTokenUsedToCallWebAPI' key already exists.
40+
// This key is required to acquire Access Token for Graph Service Client.
3741
if (!context.HttpContext.Items.ContainsKey("JwtSecurityTokenUsedToCallWebAPI"))
3842
{
39-
// extract the cached AT that was presented to the Web API
43+
// For Web App, access token is retrieved using account identifier. But at this point account identifier is null.
44+
// So, SecurityToken is saved in 'JwtSecurityTokenUsedToCallWebAPI' key.
45+
// The key is then used to get the Access Token on-behalf of user.
4046
context.HttpContext.Items.Add("JwtSecurityTokenUsedToCallWebAPI", context.SecurityToken as JwtSecurityToken);
4147
}
4248

43-
// We do not want to pull all attributes of a group from MS Graph, so we use a 'select' to just pick the ones we need.
49+
// The properties that we want to retrieve from MemberOf endpoint.
4450
string select = "id,displayName,onPremisesNetBiosName,onPremisesDomainName,onPremisesSamAccountNameonPremisesSecurityIdentifier";
45-
46-
// TODO: this line needs a try-catch, with the exception error message being "A call to Microsoft Graph failed, the error is <whatever>"
47-
// Make a Graph call to get groups and directory roles that the user is a direct member of.
48-
var memberPage = await graph.Me.MemberOf.Request().Select(select).GetAsync().ConfigureAwait(false);
49-
51+
52+
IUserMemberOfCollectionWithReferencesPage memberPage = new UserMemberOfCollectionWithReferencesPage();
53+
try
54+
{
55+
//Request to get groups and directory roles that the user is a direct member of.
56+
memberPage = await graphClient.Me.MemberOf.Request().Select(select).GetAsync().ConfigureAwait(false);
57+
}
58+
catch(Exception graphEx)
59+
{
60+
var exMsg = graphEx.InnerException != null ? graphEx.InnerException.Message : graphEx.Message;
61+
Console.WriteLine("Call to Microsoft Graph failed: "+ exMsg);
62+
}
5063
if (memberPage?.Count > 0)
5164
{
52-
// If the result is paginated, this method will process all the pages for us.
65+
// There is a limit to number of groups returned, below method make calls to Microsoft graph to get all the groups.
5366
var allgroups = ProcessIGraphServiceMemberOfCollectionPage(memberPage);
5467

5568
if (allgroups?.Count > 0)
@@ -85,11 +98,12 @@ public static async Task ProcessClaimsForGroupsOverage(TokenValidatedContext con
8598
}
8699
finally
87100
{
101+
// Checks if the key 'JwtSecurityTokenUsedToCallWebAPI' exists.
88102
if (context.HttpContext.Items.ContainsKey("JwtSecurityTokenUsedToCallWebAPI"))
89103
{
90-
// TODO: The following comment makes no sense !
91-
// Remove the key as Microsoft.Identity.Web library utilizes this key.
104+
// Removes 'JwtSecurityTokenUsedToCallWebAPI' from Items collection.
92105
// If not removed then it can cause failure to the application.
106+
// Because this key is also added by StoreTokenUsedToCallWebAPI method of Microsoft.Identity.Web.
93107
context.HttpContext.Items.Remove("JwtSecurityTokenUsedToCallWebAPI");
94108
}
95109
}
@@ -100,9 +114,9 @@ public static async Task ProcessClaimsForGroupsOverage(TokenValidatedContext con
100114
/// </summary>
101115
/// <param name="context"></param>
102116
/// <param name="identity"></param>
103-
private static void RemoveExistingClaim(ClaimsIdentity identity)
117+
private static void RemoveExistingClaims(ClaimsIdentity identity)
104118
{
105-
// clear an existing claim
119+
//clear existing claim
106120
List<Claim> existingGroupsClaims = identity.Claims.Where(x => x.Type == "groups").ToList();
107121
if (existingGroupsClaims?.Count > 0)
108122
{

5-WebApp-AuthZ/5-2-Groups/Startup.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Microsoft.Identity.Web;
1111
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
1212
using Microsoft.Identity.Web.UI;
13+
using WebApp_OpenIDConnect_DotNet.Infrastructure;
1314
using WebApp_OpenIDConnect_DotNet.Services.GroupProcessing;
1415

1516
namespace WebApp_OpenIDConnect_DotNet
@@ -26,6 +27,7 @@ public Startup(IConfiguration configuration)
2627
// This method gets called by the runtime. Use this method to add services to the container.
2728
public void ConfigureServices(IServiceCollection services)
2829
{
30+
var initialScopes = new string[] { Constants.ScopeUserRead, Constants.ScopeGroupMemberRead };
2931
services.Configure<CookiePolicyOptions>(options =>
3032
{
3133
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
@@ -48,7 +50,7 @@ public void ConfigureServices(IServiceCollection services)
4850
await GraphHelper.ProcessClaimsForGroupsOverage(context);
4951
};
5052
}, options => { Configuration.Bind("AzureAd", options); })
51-
.EnableTokenAcquisitionToCallDownstreamApi(options => Configuration.Bind("AzureAd", options))
53+
.EnableTokenAcquisitionToCallDownstreamApi(options => Configuration.Bind("AzureAd", options), initialScopes)
5254
.AddMicrosoftGraph(Configuration.GetSection("GraphAPI"))
5355
.AddInMemoryTokenCaches();
5456

5-WebApp-AuthZ/5-2-Groups/appsettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@
2222
}
2323
},
2424
"AllowedHosts": "*",
25-
"GraphApiUrl": "https://graph.microsoft.com/beta"
25+
"GraphApiUrl": "https://graph.microsoft.com/v1.0"
2626
}

0 commit comments

Comments
 (0)