diff --git a/src/dotnet/Common/Constants/Authentication/EntraUserClaimConstants.cs b/src/dotnet/Common/Constants/Authentication/EntraUserClaimConstants.cs
new file mode 100644
index 0000000000..289b77f959
--- /dev/null
+++ b/src/dotnet/Common/Constants/Authentication/EntraUserClaimConstants.cs
@@ -0,0 +1,13 @@
+namespace FoundationaLLM.Common.Constants.Authentication
+{
+ ///
+ /// Additional claim constants that are not included in .
+ ///
+ public static class EntraUserClaimConstants
+ {
+ ///
+ /// Groups claim.
+ ///
+ public const string Groups = "groups";
+ }
+}
diff --git a/src/dotnet/Common/Constants/Instance/SecurityGroupRetrievalStrategies.cs b/src/dotnet/Common/Constants/Instance/SecurityGroupRetrievalStrategies.cs
index 65cf0c09df..518cec3d81 100644
--- a/src/dotnet/Common/Constants/Instance/SecurityGroupRetrievalStrategies.cs
+++ b/src/dotnet/Common/Constants/Instance/SecurityGroupRetrievalStrategies.cs
@@ -14,5 +14,10 @@ public static class SecurityGroupRetrievalStrategies
/// Identity management service.
///
public const string IdentityManagementService = "IdentityManagementService";
+
+ ///
+ /// Access token groups claim.
+ ///
+ public const string AccessToken = "AccessToken";
}
}
diff --git a/src/dotnet/Common/Interfaces/IUserClaimsProviderService.cs b/src/dotnet/Common/Interfaces/IUserClaimsProviderService.cs
index b248cf9666..d8ed34e4d2 100644
--- a/src/dotnet/Common/Interfaces/IUserClaimsProviderService.cs
+++ b/src/dotnet/Common/Interfaces/IUserClaimsProviderService.cs
@@ -28,5 +28,14 @@ public interface IUserClaimsProviderService
///
/// The object providing details about the security principal.
bool IsServicePrincipal(ClaimsPrincipal userPrincipal);
+
+ ///
+ /// Returns a list of security group identifiers from the provided
+ /// .
+ ///
+ /// The principal that provides multiple
+ /// claims-based identities.
+ ///
+ List? GetSecurityGroupIds(ClaimsPrincipal? userPrincipal);
}
}
diff --git a/src/dotnet/Common/Middleware/CallContextMiddleware.cs b/src/dotnet/Common/Middleware/CallContextMiddleware.cs
index 317b014974..eb1acf3bf4 100644
--- a/src/dotnet/Common/Middleware/CallContextMiddleware.cs
+++ b/src/dotnet/Common/Middleware/CallContextMiddleware.cs
@@ -45,14 +45,24 @@ public async Task InvokeAsync(
// Extract from ClaimsPrincipal if available:
callContext.CurrentUserIdentity = claimsProviderService.GetUserIdentity(context.User);
+ // We are only expanding group membership for User objects
+ // Service Principal permissions must be assigned directly and not over group membership.
if (callContext.CurrentUserIdentity != null
- && !claimsProviderService.IsServicePrincipal(context.User)
- && instanceSettings.Value.SecurityGroupRetrievalStrategy == SecurityGroupRetrievalStrategies.IdentityManagementService)
+ && !claimsProviderService.IsServicePrincipal(context.User))
{
- // We are only expanding group membership for User objects
- // Service Principal permissions must be assigned directly and not over group membership.
- callContext.CurrentUserIdentity.GroupIds = await identityManagementService.GetGroupsForPrincipal(
- callContext.CurrentUserIdentity.UserId!);
+ switch(instanceSettings.Value.SecurityGroupRetrievalStrategy)
+ {
+ case SecurityGroupRetrievalStrategies.IdentityManagementService:
+ callContext.CurrentUserIdentity.GroupIds = await identityManagementService.GetGroupsForPrincipal(
+ callContext.CurrentUserIdentity.UserId!);
+ break;
+ case SecurityGroupRetrievalStrategies.AccessToken:
+ callContext.CurrentUserIdentity.GroupIds = claimsProviderService.GetSecurityGroupIds(context.User) ?? [];
+ break;
+ case SecurityGroupRetrievalStrategies.None:
+ default:
+ break;
+ }
}
}
else
diff --git a/src/dotnet/Common/Services/Security/EntraUserClaimsProviderService.cs b/src/dotnet/Common/Services/Security/EntraUserClaimsProviderService.cs
index a164126301..278c2ca7ad 100644
--- a/src/dotnet/Common/Services/Security/EntraUserClaimsProviderService.cs
+++ b/src/dotnet/Common/Services/Security/EntraUserClaimsProviderService.cs
@@ -1,4 +1,5 @@
-using FoundationaLLM.Common.Interfaces;
+using FoundationaLLM.Common.Constants.Authentication;
+using FoundationaLLM.Common.Interfaces;
using FoundationaLLM.Common.Models.Authentication;
using Microsoft.Identity.Web;
using System.Security.Claims;
@@ -27,6 +28,16 @@ public class EntraUserClaimsProviderService : IUserClaimsProviderService
};
}
+ ///
+ public List? GetSecurityGroupIds(ClaimsPrincipal? userPrincipal)
+ {
+ if (userPrincipal == null)
+ {
+ return null;
+ }
+ return userPrincipal.Claims.Where(c => c.Type == EntraUserClaimConstants.Groups).Select(x => x?.Value).ToList()!;
+ }
+
///
public bool IsServicePrincipal(ClaimsPrincipal userPrincipal) =>
// Service Principal tokens do not have a "scp" claim
diff --git a/src/dotnet/Common/Services/Security/NoOpUserClaimsProviderService.cs b/src/dotnet/Common/Services/Security/NoOpUserClaimsProviderService.cs
index 1a4e566f75..00733ce808 100644
--- a/src/dotnet/Common/Services/Security/NoOpUserClaimsProviderService.cs
+++ b/src/dotnet/Common/Services/Security/NoOpUserClaimsProviderService.cs
@@ -1,11 +1,6 @@
using FoundationaLLM.Common.Interfaces;
using FoundationaLLM.Common.Models.Authentication;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Security.Claims;
-using System.Text;
-using System.Threading.Tasks;
namespace FoundationaLLM.Common.Services.Security
{
@@ -18,6 +13,9 @@ public class NoOpUserClaimsProviderService : IUserClaimsProviderService
///
public UnifiedUserIdentity? GetUserIdentity(ClaimsPrincipal? userPrincipal) => null;
+ ///
+ public List? GetSecurityGroupIds(ClaimsPrincipal? userPrincipal) => null;
+
///
public bool IsServicePrincipal(ClaimsPrincipal userPrincipal) => false;
}