diff --git a/src/Service.Tests/AuthTestHelper.cs b/src/Service.Tests/AuthTestHelper.cs
index cd1e91422c..88f080ea45 100644
--- a/src/Service.Tests/AuthTestHelper.cs
+++ b/src/Service.Tests/AuthTestHelper.cs
@@ -21,7 +21,8 @@ internal static class AuthTestHelper
///
public static string CreateAppServiceEasyAuthToken(
string? nameClaimType = ClaimTypes.Name,
- string? roleClaimType = ClaimTypes.Role)
+ string? roleClaimType = ClaimTypes.Role,
+ IEnumerable? additionalClaims = null)
{
AppServiceClaim emailClaim = new()
{
@@ -65,7 +66,7 @@ public static string CreateAppServiceEasyAuthToken(
Typ = ClaimTypes.Name
};
- List claims = new()
+ HashSet claims = new()
{
emailClaim,
roleClaimAnonymous,
@@ -76,6 +77,11 @@ public static string CreateAppServiceEasyAuthToken(
nameUriClaimType
};
+ if (additionalClaims != null)
+ {
+ claims.UnionWith(additionalClaims);
+ }
+
AppServiceClientPrincipal token = new()
{
Auth_typ = "aad",
@@ -98,7 +104,8 @@ public static string CreateAppServiceEasyAuthToken(
public static string CreateStaticWebAppsEasyAuthToken(
bool addAuthenticated = true,
string? specificRole = null,
- IEnumerable? claims = null)
+ string? userId = null,
+ string? userDetails = null)
{
// The anonymous role is present in all requests sent to Static Web Apps or AppService endpoints.
List roles = new()
@@ -130,9 +137,10 @@ public static string CreateStaticWebAppsEasyAuthToken(
StaticWebAppsClientPrincipal token = new()
{
- IdentityProvider = "github",
- UserRoles = roles,
- Claims = claims
+ UserId = userId,
+ UserDetails = userDetails,
+ IdentityProvider = "aad",
+ UserRoles = roles
};
string serializedToken = JsonSerializer.Serialize(value: token);
diff --git a/src/Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs b/src/Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
index fbf29323bc..286b1670cd 100644
--- a/src/Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
+++ b/src/Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
@@ -5,6 +5,7 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Azure.DataApiBuilder.Config;
+using Azure.DataApiBuilder.Service.AuthenticationHelpers;
using Azure.DataApiBuilder.Service.Authorization;
using Azure.DataApiBuilder.Service.Tests.Authentication.Helpers;
using Microsoft.AspNetCore.Http;
@@ -12,7 +13,7 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using static Azure.DataApiBuilder.Service.AuthenticationHelpers.StaticWebAppsAuthentication;
+using static Azure.DataApiBuilder.Service.AuthenticationHelpers.AppServiceAuthentication;
namespace Azure.DataApiBuilder.Service.Tests.Authentication
{
@@ -180,16 +181,14 @@ public async Task TestValidStaticWebAppsEasyAuthToken(bool sendAuthorizationHead
}
///
- /// Ensures SWA token payload claims are processed by
- /// validating that those claims are present
+ /// Ensures AppService EasyAuth payload claims are processed by validating that those claims are present
/// on the authenticated .NET ClaimsPrincipal object.
- /// Demonstrates using the immutable claim values tid and oid
- /// as a combined key for uniquely identifying the API's data
- /// and determining whether a user should be granted access to that data.
+ /// Demonstrates using the immutable claim values tid and oid as a combined key for uniquely identifying
+ /// the API's data and determining whether a user should be granted access to that data.
///
///
[TestMethod]
- public async Task TestStaticWebAppsEasyAuthTokenClaims()
+ public async Task TestAppServiceEasyAuthTokenClaims()
{
string objectIdClaimType = "oid";
string objectId = "f35eaa76-b8e6-4c7c-99a2-5aeeeee9ba58";
@@ -197,18 +196,17 @@ public async Task TestStaticWebAppsEasyAuthTokenClaims()
string tenantIdClaimType = "tid";
string tenantId = "8f902aef-2c06-42c9-a3d0-bc31f04a3dca";
- List payloadClaims = new();
- payloadClaims.Add(new SWAPrincipalClaim() { Typ = objectIdClaimType, Val = objectId });
- payloadClaims.Add(new SWAPrincipalClaim() { Typ = tenantIdClaimType, Val = tenantId });
+ List payloadClaims = new()
+ {
+ new AppServiceClaim() { Typ = objectIdClaimType, Val = objectId },
+ new AppServiceClaim() { Typ = tenantIdClaimType, Val = tenantId }
+ };
- string generatedToken = AuthTestHelper.CreateStaticWebAppsEasyAuthToken(
- addAuthenticated: true,
- claims: payloadClaims
- );
+ string generatedToken = AuthTestHelper.CreateAppServiceEasyAuthToken(additionalClaims: payloadClaims);
HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(
generatedToken,
- EasyAuthType.StaticWebApps);
+ EasyAuthType.AppService);
Assert.IsNotNull(postMiddlewareContext.User.Identity);
Assert.IsTrue(postMiddlewareContext.User.Identity.IsAuthenticated);
@@ -218,11 +216,9 @@ public async Task TestStaticWebAppsEasyAuthTokenClaims()
}
///
- /// Validates that null claim type and/or null claim value
- /// are not processed claims on the .NET ClaimsPrincipal object,
- /// due lack of null claim type/ claim value support on the ClaimsPrincipal.
- /// Validates that empty string for claim type and/or value
- /// is processed successfully.
+ /// Validates that null claim type and/or null claim value are not processed claims on the
+ /// .NET ClaimsPrincipal object, due lack of null claim type/ claim value support on the ClaimsPrincipal.
+ /// Validates that empty string for claim type and/or value is processed successfully.
///
/// string representation of claim type
/// string representation of claim value
@@ -236,19 +232,20 @@ public async Task TestStaticWebAppsEasyAuthTokenClaims()
[DataRow("tid", "", true, DisplayName = "Claim value empty string - will process")]
[DataRow("", "", true, DisplayName = "Claim type/value empty string - will process")]
- public async Task TestStaticWebAppsEasyAuth_IncompleteTokenClaims(string? claimType, string? claimValue, bool expectProcessedClaim)
+ public async Task TestAppServiceEasyAuth_IncompleteTokenClaims(string? claimType, string? claimValue, bool expectProcessedClaim)
{
- List payloadClaims = new();
- payloadClaims.Add(new SWAPrincipalClaim() { Typ = claimType, Val = claimValue });
+ List payloadClaims = new()
+ {
+ new AppServiceClaim() { Typ = claimType, Val = claimValue }
+ };
- string generatedToken = AuthTestHelper.CreateStaticWebAppsEasyAuthToken(
- addAuthenticated: true,
- claims: payloadClaims
+ string generatedToken = AuthTestHelper.CreateAppServiceEasyAuthToken(
+ additionalClaims: payloadClaims
);
HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(
generatedToken,
- EasyAuthType.StaticWebApps);
+ EasyAuthType.AppService);
Assert.IsNotNull(postMiddlewareContext.User.Identity);
Assert.IsTrue(postMiddlewareContext.User.Identity.IsAuthenticated);
@@ -262,6 +259,40 @@ public async Task TestStaticWebAppsEasyAuth_IncompleteTokenClaims(string? claimT
Assert.AreEqual(expected: (int)HttpStatusCode.OK, actual: postMiddlewareContext.Response.StatusCode);
}
+ ///
+ /// Tests that a populated userId and/or userDetails property on the SWA Authenticated user payload
+ /// results in the created ClaimsIdentity object having a matching claim for the populated property.
+ ///
+ /// SWA userId property value.
+ /// SWA userDetails property value.
+ /// Whether claim matching property should be present on ClaimsIdentity object.
+ [DataTestMethod]
+ [DataRow("1337", "UserDetailsString", true, DisplayName = "UserId and UserDetails Claims Match SWA User Payload")]
+ [DataRow("", "", false, DisplayName = "Empty properties in SWA User Payload -> No Matching Claims")]
+ [DataRow(null, null, false, DisplayName = "Null properties in SWA User Payload -> No Matching Claims")]
+ public async Task TestStaticWebAppsEasyAuthToken_PropertiesToClaims(string userId, string userDetails, bool expectClaim)
+ {
+ string generatedToken = AuthTestHelper.CreateStaticWebAppsEasyAuthToken(addAuthenticated: true, userId: userId, userDetails: userDetails);
+ HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(generatedToken, EasyAuthType.StaticWebApps);
+
+ Assert.IsNotNull(postMiddlewareContext.User.Identity);
+ Assert.IsTrue(postMiddlewareContext.User.Identity.IsAuthenticated);
+ Assert.AreEqual(expected: (int)HttpStatusCode.OK, actual: postMiddlewareContext.Response.StatusCode);
+
+ // If userId and/or userDetails are null in the EasyAuth payload, a claim will NOT be added to the ClaimsIdentity object
+ // for the null/empty/whitespace property.
+ if (expectClaim)
+ {
+ Assert.IsTrue(postMiddlewareContext.User.HasClaim(type: StaticWebAppsAuthentication.USER_ID_CLAIM, value: userId));
+ Assert.IsTrue(postMiddlewareContext.User.HasClaim(type: StaticWebAppsAuthentication.USER_DETAILS_CLAIM, value: userDetails));
+ }
+ else
+ {
+ Assert.IsFalse(postMiddlewareContext.User.HasClaim(type: StaticWebAppsAuthentication.USER_ID_CLAIM, value: string.Empty));
+ Assert.IsFalse(postMiddlewareContext.User.HasClaim(type: StaticWebAppsAuthentication.USER_DETAILS_CLAIM, value: string.Empty));
+ }
+ }
+
///
/// When the user request is a valid token but only has an anonymous role,
/// we still return OK. We assign the client role header to be anonymous.
diff --git a/src/Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs b/src/Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
index 0c1a991173..a1b813d2e5 100644
--- a/src/Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
+++ b/src/Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
@@ -17,6 +17,9 @@ namespace Azure.DataApiBuilder.Service.AuthenticationHelpers
///
public class StaticWebAppsAuthentication
{
+ public const string USER_ID_CLAIM = "userId";
+ public const string USER_DETAILS_CLAIM = "userDetails";
+
///
/// Link for reference of how StaticWebAppsClientPrincipal is defined
/// https://docs.microsoft.com/azure/static-web-apps/user-information?tabs=csharp#client-principal-data
@@ -27,16 +30,6 @@ public class StaticWebAppsClientPrincipal
public string? UserId { get; set; }
public string? UserDetails { get; set; }
public IEnumerable? UserRoles { get; set; }
- public IEnumerable? Claims { get; set; }
- }
-
- ///
- /// Representation of a user claim in a SWA token payload.
- ///
- public class SWAPrincipalClaim
- {
- public string? Typ { get; set; }
- public string? Val { get; set; }
}
///
@@ -67,23 +60,22 @@ public class SWAPrincipalClaim
return identity;
}
- identity = new(principal.IdentityProvider, nameType: ClaimTypes.Name, roleType: AuthenticationConfig.ROLE_CLAIM_TYPE);
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, principal.UserId ?? string.Empty));
- identity.AddClaim(new Claim(ClaimTypes.Name, principal.UserDetails ?? string.Empty));
+ identity = new(authenticationType: principal.IdentityProvider, nameType: USER_ID_CLAIM, roleType: AuthenticationConfig.ROLE_CLAIM_TYPE);
- // output identity.Claims
- // [0] { Type = "role", Value = "roleName" }
- identity.AddClaims(principal.UserRoles.Select(roleName => new Claim(AuthenticationConfig.ROLE_CLAIM_TYPE, roleName)));
+ if (!string.IsNullOrWhiteSpace(principal.UserId))
+ {
+ identity.AddClaim(new Claim(USER_ID_CLAIM, principal.UserId));
+ }
- // Copy all SWA token claims to .NET ClaimsIdentity object.
- if (principal.Claims is not null && principal.Claims.Any())
+ if (!string.IsNullOrWhiteSpace(principal.UserDetails))
{
- identity.AddClaims(principal.Claims
- .Where(claim => claim.Typ is not null && claim.Val is not null)
- .Select(claim => new Claim(type: claim.Typ!, value: claim.Val!))
- );
+ identity.AddClaim(new Claim(USER_DETAILS_CLAIM, principal.UserDetails));
}
+ // output identity.Claims
+ // [0] { Type = "roles", Value = "roleName" }
+ identity.AddClaims(principal.UserRoles.Select(roleName => new Claim(AuthenticationConfig.ROLE_CLAIM_TYPE, roleName)));
+
return identity;
}
catch (Exception error) when (