diff --git a/.gitignore b/.gitignore
index 1318374c..08600373 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,9 +39,6 @@
/2-WebApp-graph-user/2-2-TokenCache/.vs
/2-WebApp-graph-user/2-2-TokenCache/bin
/2-WebApp-graph-user/2-2-TokenCache/obj
-/2-WebApp-graph-user/2-3-Best-Practices/.vs
-/2-WebApp-graph-user/2-3-Best-Practices/bin
-/2-WebApp-graph-user/2-3-Best-Practices/obj
/2-WebApp-graph-user/2-3-Multi-Tenant/.vs
/2-WebApp-graph-user/2-3-Multi-Tenant/bin
/2-WebApp-graph-user/2-3-Multi-Tenant/obj
diff --git a/4-WebApp-your-API/Client/Properties/launchSettings.json b/4-WebApp-your-API/Client/Properties/launchSettings.json
index f1f25b7f..f0c28a9b 100644
--- a/4-WebApp-your-API/Client/Properties/launchSettings.json
+++ b/4-WebApp-your-API/Client/Properties/launchSettings.json
@@ -3,8 +3,8 @@
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
- "applicationUrl": "https://localhost:44316/",
- "sslPort": 44316
+ "applicationUrl": "https://localhost:5000/",
+ "sslPort": 5000
}
},
"profiles": {
@@ -14,7 +14,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
- "applicationUrl": "https://localhost:44316"
+ "applicationUrl": "https://localhost:5000"
}
}
}
\ No newline at end of file
diff --git a/4-WebApp-your-API/Client/Startup.cs b/4-WebApp-your-API/Client/Startup.cs
index 84faec2a..557e013b 100644
--- a/4-WebApp-your-API/Client/Startup.cs
+++ b/4-WebApp-your-API/Client/Startup.cs
@@ -43,7 +43,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddOptions();
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
- .AddSignIn("AzureAdB2C", Configuration, options => Configuration.Bind("AzureAdB2C", options));
+ .AddSignIn("AzureAdB2C", Configuration, options => Configuration.Bind("AzureAdB2C", options), subscribeToOpenIdConnectMiddlewareDiagnosticsEvents:true);
// This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
// By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
diff --git a/4-WebApp-your-API/Client/appsettings.json b/4-WebApp-your-API/Client/appsettings.json
index f035bb93..ab89c863 100644
--- a/4-WebApp-your-API/Client/appsettings.json
+++ b/4-WebApp-your-API/Client/appsettings.json
@@ -1,12 +1,13 @@
{
"AzureAdB2C": {
"Instance": "https://fabrikamb2c.b2clogin.com",
- "ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
+ "ClientId": "fdb91ff5-5ce6-41f3-bdbd-8267c817015d",
"Domain": "fabrikamb2c.onmicrosoft.com",
"SignedOutCallbackPath": "/signout/B2C_1_susi",
"SignUpSignInPolicyId": "b2c_1_susi",
"ResetPasswordPolicyId": "b2c_1_reset",
- "EditProfilePolicyId": "b2c_1_edit_profile" // Optional profile editing policy
+ "EditProfilePolicyId": "b2c_1_edit_profile", // Optional profile editing policy
+ "ClientSecret": "X330F3#92!z614M4"
//"CallbackPath": "/signin/B2C_1_sign_up_in" // defaults to /signin-oidc
},
"AzureAd": {
diff --git a/4-WebApp-your-API/WebApp-OpenIDConnect-DotNet.sln b/4-WebApp-your-API/WebApp-OpenIDConnect-DotNet.sln
index 8bb77477..c59f0994 100644
--- a/4-WebApp-your-API/WebApp-OpenIDConnect-DotNet.sln
+++ b/4-WebApp-your-API/WebApp-OpenIDConnect-DotNet.sln
@@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web", ".
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.UI", "..\Microsoft.Identity.Web.UI\Microsoft.Identity.Web.UI.csproj", "{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Test", "..\Microsoft.Identity.Web.Test\Microsoft.Identity.Web.Test.csproj", "{C4827ACC-3765-46C2-8D86-ACFCEB175A11}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2B0D2D4-6799-4B6F-AC51-F0F0A6CA1E89}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4827ACC-3765-46C2-8D86-ACFCEB175A11}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Microsoft.Identity.Web.Test/WebApiServiceCollectionExtensionsTests.cs b/Microsoft.Identity.Web.Test/WebApiServiceCollectionExtensionsTests.cs
index fba6c594..d2e0bbfa 100644
--- a/Microsoft.Identity.Web.Test/WebApiServiceCollectionExtensionsTests.cs
+++ b/Microsoft.Identity.Web.Test/WebApiServiceCollectionExtensionsTests.cs
@@ -46,24 +46,31 @@ public void TestAuthority()
public void TestAudience()
{
JwtBearerOptions options = new JwtBearerOptions();
+ MicrosoftIdentityOptions microsoftIdentityOptions = new MicrosoftIdentityOptions() { ClientId = Guid.NewGuid().ToString() };
// Act and Assert
options.Audience = "https://localhost";
- WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
+ WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 1);
Assert.True(options.TokenValidationParameters.ValidAudiences.First() == "https://localhost");
options.Audience = "api://1EE5A092-0DFD-42B6-88E5-C517C0141321";
- WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
+ WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 1);
Assert.True(options.TokenValidationParameters.ValidAudiences.First() == "api://1EE5A092-0DFD-42B6-88E5-C517C0141321");
options.Audience = "1EE5A092-0DFD-42B6-88E5-C517C0141321";
- WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
+ WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 2);
Assert.Contains("api://1EE5A092-0DFD-42B6-88E5-C517C0141321", options.TokenValidationParameters.ValidAudiences);
Assert.Contains("1EE5A092-0DFD-42B6-88E5-C517C0141321", options.TokenValidationParameters.ValidAudiences);
+ options.Audience = null;
+ WebApiServiceCollectionExtensions.EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
+ Assert.True(options.TokenValidationParameters.ValidAudiences.Count() == 2);
+ Assert.Contains($"api://{microsoftIdentityOptions.ClientId}", options.TokenValidationParameters.ValidAudiences);
+ Assert.Contains($"{microsoftIdentityOptions.ClientId}", options.TokenValidationParameters.ValidAudiences);
+
}
}
}
diff --git a/Microsoft.Identity.Web/ClaimConstants.cs b/Microsoft.Identity.Web/ClaimConstants.cs
index f332da4e..390f1918 100644
--- a/Microsoft.Identity.Web/ClaimConstants.cs
+++ b/Microsoft.Identity.Web/ClaimConstants.cs
@@ -15,6 +15,7 @@ public static class ClaimConstants
public const string TenantId = "http://schemas.microsoft.com/identity/claims/tenantid";
public const string Tid = "tid";
public const string ClientInfo = "client_info";
+ public const string UniqueObjectIdentifier = "utid";
// Older scope claim
public const string Scope = "http://schemas.microsoft.com/identity/claims/scope";
// Newer scope claim
diff --git a/Microsoft.Identity.Web/ClaimsPrincipalExtensions.cs b/Microsoft.Identity.Web/ClaimsPrincipalExtensions.cs
index 6c04e5e2..579af09b 100644
--- a/Microsoft.Identity.Web/ClaimsPrincipalExtensions.cs
+++ b/Microsoft.Identity.Web/ClaimsPrincipalExtensions.cs
@@ -28,7 +28,7 @@ public static string GetMsalAccountId(this ClaimsPrincipal claimsPrincipal)
!string.IsNullOrWhiteSpace(userFlowId))
{
// B2C pattern: {oid}-{userFlow}.{tid}
- return $"{nameIdentifierId}-{userFlowId}.{tenantId}";
+ return $"{nameIdentifierId}.{tenantId}";
}
else if (!string.IsNullOrWhiteSpace(userObjectId) && !string.IsNullOrWhiteSpace(tenantId))
{
@@ -152,12 +152,7 @@ public static string GetUserFlowId(this ClaimsPrincipal claimsPrincipal)
/// Name identifier ID (sub) of the identity, or null if it cannot be found
public static string GetNameIdentifierId(this ClaimsPrincipal claimsPrincipal)
{
- string sub = claimsPrincipal.FindFirstValue(ClaimConstants.Sub);
- if (string.IsNullOrEmpty(sub))
- {
- sub = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
- }
- return sub;
+ return claimsPrincipal.FindFirstValue(ClaimConstants.UniqueObjectIdentifier);
}
}
}
diff --git a/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs b/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs
index 4f8c8a6c..2fea242b 100644
--- a/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs
+++ b/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs
@@ -171,10 +171,8 @@ public static AuthenticationBuilder AddProtectedWebApi(
// The valid audience could be given as Client Id or as Uri.
// If it does not start with 'api://', this variant is added to the list of valid audiences.
- EnsureValidAudiencesContainsApiGuidIfGuidProvided(options);
+ EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions);
- options.TokenValidationParameters.ValidAudiences = GetValidAudiences(options, microsoftIdentityOptions);
-
// If the developer registered an IssuerValidator, do not overwrite it
if (options.TokenValidationParameters.IssuerValidator == null)
{
@@ -270,13 +268,21 @@ internal static void EnsureAuthorityIsV2_0(JwtBearerOptions options)
///
/// Jwt bearer options for which to ensure that
/// api://GUID is a valid audience
- internal static void EnsureValidAudiencesContainsApiGuidIfGuidProvided(JwtBearerOptions options)
+ internal static void EnsureValidAudiencesContainsApiGuidIfGuidProvided(JwtBearerOptions options, MicrosoftIdentityOptions msIdentityOptions)
{
- var validAudiences = new List { options.Audience };
- if (!options.Audience.StartsWith("api://", StringComparison.OrdinalIgnoreCase)
- && Guid.TryParse(options.Audience, out _))
- validAudiences.Add($"api://{options.Audience}");
-
+ var validAudiences = new List();
+ if (!string.IsNullOrWhiteSpace(options.Audience))
+ {
+ validAudiences.Add(options.Audience);
+ if (!options.Audience.StartsWith("api://", StringComparison.OrdinalIgnoreCase)
+ && Guid.TryParse(options.Audience, out _))
+ validAudiences.Add($"api://{options.Audience}");
+ }
+ else
+ {
+ validAudiences.Add(msIdentityOptions.ClientId);
+ validAudiences.Add($"api://{msIdentityOptions.ClientId}");
+ }
options.TokenValidationParameters.ValidAudiences = validAudiences;
}
}
diff --git a/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs b/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs
index 5da93a31..74069341 100644
--- a/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs
+++ b/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs
@@ -122,6 +122,7 @@ public static IServiceCollection AddWebAppCallsProtectedWebApi(
if (clientInfoFromServer != null)
{
context.Principal.Identities.FirstOrDefault().AddClaim(new Claim(ClaimConstants.Tid, clientInfoFromServer.UniqueTenantIdentifier));
+ context.Principal.Identities.FirstOrDefault().AddClaim(new Claim(ClaimConstants.UniqueObjectIdentifier, clientInfoFromServer.UniqueObjectIdentifier));
}
}
}