diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs b/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs
index 360be6c8..7e4d7c52 100644
--- a/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs
+++ b/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs
@@ -39,6 +39,13 @@ internal static class KeycloakClientApiConstants
internal const string SendVerifyEmail = $"{GetRealm}/users/{{id}}/send-verify-email";
internal const string ExecuteActionsEmail = $"{GetRealm}/users/{{id}}/execute-actions-email";
+ internal const string GetUserGroups = $"{GetRealm}/users/{{id}}/groups";
+
+
+
+ internal const string GetGroups = $"{GetRealm}/groups";
+
+ internal const string GetGroup = $"{GetRealm}/groups/{{id}}";
#endregion
}
diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs
index c0b9ee90..cfeeb70e 100644
--- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs
+++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs
@@ -7,6 +7,6 @@ namespace Keycloak.AuthServices.Sdk.Admin;
/// Aggregates multiple clients. and
///
public interface IKeycloakClient
- : IKeycloakRealmClient, IKeycloakProtectedResourceClient, IKeycloakUserClient
+ : IKeycloakRealmClient, IKeycloakProtectedResourceClient, IKeycloakUserClient, IKeycloakGroupClient
{
}
diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs
new file mode 100644
index 00000000..acd65722
--- /dev/null
+++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs
@@ -0,0 +1,33 @@
+namespace Keycloak.AuthServices.Sdk.Admin;
+
+using System.Threading.Tasks;
+using Constants;
+using Models;
+using Refit;
+using Requests.Groups;
+
+///
+/// Group management
+///
+[Headers("Accept: application/json")]
+public interface IKeycloakGroupClient
+{
+
+ ///
+ /// Get a stream of groups on the realm.
+ ///
+ /// Realm name.
+ /// Optional query parameters.
+ /// A stream of groups, filtered according to query parameters.
+ [Get(KeycloakClientApiConstants.GetGroups)]
+ Task> GetGroups(string realm, [Query] GetGroupRequestParameters? parameters = default);
+
+ ///
+ /// Get representation of a group.
+ ///
+ /// Realm name.
+ /// group ID.
+ /// The group representation.
+ [Get(KeycloakClientApiConstants.GetGroup)]
+ Task GetGroup(string realm, [AliasAs("id")] string groupId);
+}
diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs
index 61e36d9d..0d3e52a5 100644
--- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs
+++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs
@@ -3,6 +3,7 @@
using Constants;
using Models;
using Refit;
+using Requests.Groups;
using Requests.Users;
///
@@ -84,4 +85,14 @@ Task ExecuteActionsEmail(string realm, [AliasAs("id")] string userId,
[Query] int? lifespan = default,
[Query][AliasAs("redirect_uri")] string? redirectUri = default,
[Body] List? actions = default);
+
+ ///
+ /// Get a users's groups.
+ ///
+ /// Realm name (not ID).
+ /// User ID.
+ /// Optional query parameters.
+ /// A stream of users, filtered according to query parameters.
+ [Get(KeycloakClientApiConstants.GetUserGroups)]
+ Task> GetUserGroups(string realm, [AliasAs("id")] string userId, [Query] GetGroupRequestParameters? parameters = default);
}
diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs
new file mode 100644
index 00000000..987de774
--- /dev/null
+++ b/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs
@@ -0,0 +1,18 @@
+#pragma warning disable CS1591, CS8618
+namespace Keycloak.AuthServices.Sdk.Admin.Models;
+
+using System.Collections.Generic;
+
+///
+/// Group representation.
+///
+public class Group
+{
+ public string Id { get; init; } = default!;
+ public string? Name { get; init; }
+ public string? Path { get; init; }
+ public Dictionary? ClientRoles { get; init; }
+ public string[]? RealmRoles { get; init; }
+ public Group[]? SubGroups { get; init; }
+ public Dictionary? Attributes { get; init; }
+}
diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs
new file mode 100644
index 00000000..17637e92
--- /dev/null
+++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs
@@ -0,0 +1,71 @@
+namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups;
+
+using Keycloak.AuthServices.Sdk.Admin.Models;
+using Refit;
+
+///
+/// Optional request parameters for the endpoint.
+/// It can be called in three different ways.
+///
+/// -
+///
+/// Don’t specify any criteria. A stream of all groups within that realm will be returned (limited by pagination).
+///
+///
+/// -
+///
+/// If is specified, other criteria such as
+/// will be ignored even though you may set them. The string will be matched against
+/// the , ,
+/// and the of a .
+///
+///
+/// -
+///
+/// If is unspecified but any of , ,
+/// or are specified, then those criteria are matched against
+/// their respective fields on a entity. Combined with a logical AND.
+///
+///
+///
+///
+public class GetGroupRequestParameters
+{
+ ///
+ /// Defines whether brief representations are returned. Default is false.
+ ///
+ [AliasAs("briefRepresentation")]
+ public bool? BriefRepresentation { get; init; }
+
+ ///
+ /// Defines whether the params , ,
+ /// and must match exactly
+ ///
+ [AliasAs("exact")]
+ public bool? Exact { get; init; }
+
+ ///
+ /// Pagination offset.
+ ///
+ [AliasAs("first")]
+ public int? First { get; init; }
+
+ ///
+ /// Maximum results size. Default is 100.
+ ///
+ [AliasAs("max")]
+ public int? Max { get; init; }
+
+ ///
+ /// A query to search for custom attributes, in the format "key1:value2 key2:value2".
+ ///
+ [AliasAs("q")]
+ public string? Query { get; init; }
+
+ ///
+ /// Search for a string contained in , ,
+ /// or .
+ ///
+ [AliasAs("search")]
+ public string? Search { get; init; }
+}