Skip to content

Commit cae8c19

Browse files
authored
Merge pull request #296 from Azure-Samples/jennyf/b2cwebapi
Jennyf/b2cwebapi
2 parents f40c079 + 41c3eae commit cae8c19

File tree

10 files changed

+42
-28
lines changed

10 files changed

+42
-28
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@
3939
/2-WebApp-graph-user/2-2-TokenCache/.vs
4040
/2-WebApp-graph-user/2-2-TokenCache/bin
4141
/2-WebApp-graph-user/2-2-TokenCache/obj
42-
/2-WebApp-graph-user/2-3-Best-Practices/.vs
43-
/2-WebApp-graph-user/2-3-Best-Practices/bin
44-
/2-WebApp-graph-user/2-3-Best-Practices/obj
4542
/2-WebApp-graph-user/2-3-Multi-Tenant/.vs
4643
/2-WebApp-graph-user/2-3-Multi-Tenant/bin
4744
/2-WebApp-graph-user/2-3-Multi-Tenant/obj

4-WebApp-your-API/Client/Properties/launchSettings.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"windowsAuthentication": false,
44
"anonymousAuthentication": true,
55
"iisExpress": {
6-
"applicationUrl": "https://localhost:44316/",
7-
"sslPort": 44316
6+
"applicationUrl": "https://localhost:5000/",
7+
"sslPort": 5000
88
}
99
},
1010
"profiles": {
@@ -14,7 +14,7 @@
1414
"environmentVariables": {
1515
"ASPNETCORE_ENVIRONMENT": "Development"
1616
},
17-
"applicationUrl": "https://localhost:44316"
17+
"applicationUrl": "https://localhost:5000"
1818
}
1919
}
2020
}

4-WebApp-your-API/Client/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void ConfigureServices(IServiceCollection services)
4343
services.AddOptions();
4444

4545
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
46-
.AddSignIn("AzureAdB2C", Configuration, options => Configuration.Bind("AzureAdB2C", options));
46+
.AddSignIn("AzureAdB2C", Configuration, options => Configuration.Bind("AzureAdB2C", options), subscribeToOpenIdConnectMiddlewareDiagnosticsEvents:true);
4747

4848
// This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
4949
// By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.

4-WebApp-your-API/Client/appsettings.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"AzureAdB2C": {
33
"Instance": "https://fabrikamb2c.b2clogin.com",
4-
"ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
4+
"ClientId": "fdb91ff5-5ce6-41f3-bdbd-8267c817015d",
55
"Domain": "fabrikamb2c.onmicrosoft.com",
66
"SignedOutCallbackPath": "/signout/B2C_1_susi",
77
"SignUpSignInPolicyId": "b2c_1_susi",
88
"ResetPasswordPolicyId": "b2c_1_reset",
9-
"EditProfilePolicyId": "b2c_1_edit_profile" // Optional profile editing policy
9+
"EditProfilePolicyId": "b2c_1_edit_profile", // Optional profile editing policy
10+
"ClientSecret": "X330F3#92!z614M4"
1011
//"CallbackPath": "/signin/B2C_1_sign_up_in" // defaults to /signin-oidc
1112
},
1213
"AzureAd": {

4-WebApp-your-API/WebApp-OpenIDConnect-DotNet.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web", ".
1717
EndProject
1818
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.UI", "..\Microsoft.Identity.Web.UI\Microsoft.Identity.Web.UI.csproj", "{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}"
1919
EndProject
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Test", "..\Microsoft.Identity.Web.Test\Microsoft.Identity.Web.Test.csproj", "{C4827ACC-3765-46C2-8D86-ACFCEB175A11}"
21+
EndProject
2022
Global
2123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2224
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
3941
{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}.Debug|Any CPU.Build.0 = Debug|Any CPU
4042
{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}.Release|Any CPU.ActiveCfg = Release|Any CPU
4143
{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Release|Any CPU.Build.0 = Release|Any CPU
4248
EndGlobalSection
4349
GlobalSection(SolutionProperties) = preSolution
4450
HideSolutionNode = FALSE

Microsoft.Identity.Web.Test/WebApiServiceCollectionExtensionsTests.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,31 @@ public void TestAuthority()
4646
public void TestAudience()
4747
{
4848
JwtBearerOptions options = new JwtBearerOptions();
49+
MicrosoftIdentityOptions microsoftIdentityOptions = new MicrosoftIdentityOptions() { ClientId = Guid.NewGuid().ToString() };
4950

5051
// Act and Assert
5152
options.Audience = "https://localhost";
52-
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
53+
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
5354
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 1);
5455
Assert.True(options.TokenValidationParameters.ValidAudiences.First() == "https://localhost");
5556

5657
options.Audience = "api://1EE5A092-0DFD-42B6-88E5-C517C0141321";
57-
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
58+
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
5859
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 1);
5960
Assert.True(options.TokenValidationParameters.ValidAudiences.First() == "api://1EE5A092-0DFD-42B6-88E5-C517C0141321");
6061

6162
options.Audience = "1EE5A092-0DFD-42B6-88E5-C517C0141321";
62-
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
63+
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
6364
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 2);
6465
Assert.Contains("api://1EE5A092-0DFD-42B6-88E5-C517C0141321", options.TokenValidationParameters.ValidAudiences);
6566
Assert.Contains("1EE5A092-0DFD-42B6-88E5-C517C0141321", options.TokenValidationParameters.ValidAudiences);
6667

68+
options.Audience = null;
69+
WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
70+
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 2);
71+
Assert.Contains($"api://{microsoftIdentityOptions.ClientId}", options.TokenValidationParameters.ValidAudiences);
72+
Assert.Contains($"{microsoftIdentityOptions.ClientId}", options.TokenValidationParameters.ValidAudiences);
73+
6774
}
6875
}
6976
}

Microsoft.Identity.Web/ClaimConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public static class ClaimConstants
1515
public const string TenantId = "http://schemas.microsoft.com/identity/claims/tenantid";
1616
public const string Tid = "tid";
1717
public const string ClientInfo = "client_info";
18+
public const string UniqueObjectIdentifier = "utid";
1819
// Older scope claim
1920
public const string Scope = "http://schemas.microsoft.com/identity/claims/scope";
2021
// Newer scope claim

Microsoft.Identity.Web/ClaimsPrincipalExtensions.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static string GetMsalAccountId(this ClaimsPrincipal claimsPrincipal)
2828
!string.IsNullOrWhiteSpace(userFlowId))
2929
{
3030
// B2C pattern: {oid}-{userFlow}.{tid}
31-
return $"{nameIdentifierId}-{userFlowId}.{tenantId}";
31+
return $"{nameIdentifierId}.{tenantId}";
3232
}
3333
else if (!string.IsNullOrWhiteSpace(userObjectId) && !string.IsNullOrWhiteSpace(tenantId))
3434
{
@@ -152,12 +152,7 @@ public static string GetUserFlowId(this ClaimsPrincipal claimsPrincipal)
152152
/// <returns>Name identifier ID (sub) of the identity, or <c>null</c> if it cannot be found</returns>
153153
public static string GetNameIdentifierId(this ClaimsPrincipal claimsPrincipal)
154154
{
155-
string sub = claimsPrincipal.FindFirstValue(ClaimConstants.Sub);
156-
if (string.IsNullOrEmpty(sub))
157-
{
158-
sub = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
159-
}
160-
return sub;
155+
return claimsPrincipal.FindFirstValue(ClaimConstants.UniqueObjectIdentifier);
161156
}
162157
}
163158
}

Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,8 @@ public static AuthenticationBuilder AddProtectedWebApi(
171171

172172
// The valid audience could be given as Client Id or as Uri.
173173
// If it does not start with 'api://', this variant is added to the list of valid audiences.
174-
EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
174+
EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
175175

176-
options.TokenValidationParameters.ValidAudiences = GetValidAudiences(options, microsoftIdentityOptions);
177-
178176
// If the developer registered an IssuerValidator, do not overwrite it
179177
if (options.TokenValidationParameters.IssuerValidator == null)
180178
{
@@ -270,13 +268,21 @@ internal static void EnsureAuthorityIsV2_0(JwtBearerOptions options)
270268
/// </summary>
271269
/// <param name="options">Jwt bearer options for which to ensure that
272270
/// api://GUID is a valid audience</param>
273-
internal static void EnsureValidAudiencesContainsApiGuidIfGuidProvided(JwtBearerOptions options)
271+
internal static void EnsureValidAudiencesContainsApiGuidIfGuidProvided(JwtBearerOptions options, MicrosoftIdentityOptions msIdentityOptions)
274272
{
275-
var validAudiences = new List<string> { options.Audience };
276-
if (!options.Audience.StartsWith("api://", StringComparison.OrdinalIgnoreCase)
277-
&& Guid.TryParse(options.Audience, out _))
278-
validAudiences.Add($"api://{options.Audience}");
279-
273+
var validAudiences = new List<string>();
274+
if (!string.IsNullOrWhiteSpace(options.Audience))
275+
{
276+
validAudiences.Add(options.Audience);
277+
if (!options.Audience.StartsWith("api://", StringComparison.OrdinalIgnoreCase)
278+
&& Guid.TryParse(options.Audience, out _))
279+
validAudiences.Add($"api://{options.Audience}");
280+
}
281+
else
282+
{
283+
validAudiences.Add(msIdentityOptions.ClientId);
284+
validAudiences.Add($"api://{msIdentityOptions.ClientId}");
285+
}
280286
options.TokenValidationParameters.ValidAudiences = validAudiences;
281287
}
282288
}

Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ public static IServiceCollection AddWebAppCallsProtectedWebApi(
122122
if (clientInfoFromServer != null)
123123
{
124124
context.Principal.Identities.FirstOrDefault().AddClaim(new Claim(ClaimConstants.Tid, clientInfoFromServer.UniqueTenantIdentifier));
125+
context.Principal.Identities.FirstOrDefault().AddClaim(new Claim(ClaimConstants.UniqueObjectIdentifier, clientInfoFromServer.UniqueObjectIdentifier));
125126
}
126127
}
127128
}

0 commit comments

Comments
 (0)