commit 00bc20f8a61a857f043aea9bde80affa844a06ed
Author: Dominik Chlouba <dchlouba@students.zcu.cz>
Date:   Thu Apr 15 15:41:15 2021 +0200

    Removed unused Azure AD project, updated identity core, custom authorization middleware - permission oriented authorization
    ref #8556

diff --git a/Leuze.sln b/Leuze.sln
index 48b3590..f067c7e 100644
--- a/Leuze.sln
+++ b/Leuze.sln
@@ -35,8 +35,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{B74BED5E-F
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leuze.Tests.Core.Domain", "tests\Core\Leuze.Tests.Core.Domain\Leuze.Tests.Core.Domain.csproj", "{8ECAEA01-61F3-402D-B94E-E54FC0C94C8E}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leuze.Core.Infrastructure.ActiveDirectory", "src\Core\Infrastructure\Leuze.Core.Infrastructure.ActiveDirectory\Leuze.Core.Infrastructure.ActiveDirectory.csproj", "{1407144F-48F6-404E-A890-961B739D030D}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leuze.Tests.Core.Infrastructure.ActiveDirectory", "tests\Core\Leuze.Tests.Core.Infrastructure.ActiveDirectory\Leuze.Tests.Core.Infrastructure.ActiveDirectory.csproj", "{C1813611-5FF2-4E70-B7E5-A4E8553F0B66}"
 EndProject
 Global
@@ -77,10 +75,6 @@ Global
 		{8ECAEA01-61F3-402D-B94E-E54FC0C94C8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{8ECAEA01-61F3-402D-B94E-E54FC0C94C8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{8ECAEA01-61F3-402D-B94E-E54FC0C94C8E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{1407144F-48F6-404E-A890-961B739D030D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1407144F-48F6-404E-A890-961B739D030D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1407144F-48F6-404E-A890-961B739D030D}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{1407144F-48F6-404E-A890-961B739D030D}.Release|Any CPU.Build.0 = Release|Any CPU
 		{C1813611-5FF2-4E70-B7E5-A4E8553F0B66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C1813611-5FF2-4E70-B7E5-A4E8553F0B66}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C1813611-5FF2-4E70-B7E5-A4E8553F0B66}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -102,7 +96,6 @@ Global
 		{D037B7D3-CA28-4EBD-9E2F-3F3D347950CF} = {C5588326-929D-42B3-912B-9D838373B48E}
 		{B74BED5E-F175-4A6E-B4AC-8625E0E4E287} = {C5588326-929D-42B3-912B-9D838373B48E}
 		{8ECAEA01-61F3-402D-B94E-E54FC0C94C8E} = {B74BED5E-F175-4A6E-B4AC-8625E0E4E287}
-		{1407144F-48F6-404E-A890-961B739D030D} = {6A0F1F61-D0EB-4132-B722-5787D7046CC0}
 		{C1813611-5FF2-4E70-B7E5-A4E8553F0B66} = {B74BED5E-F175-4A6E-B4AC-8625E0E4E287}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/docs/Leuze.Core.Application.Identity.xml b/docs/Leuze.Core.Application.Identity.xml
index 0f59d23..b84f7a7 100644
--- a/docs/Leuze.Core.Application.Identity.xml
+++ b/docs/Leuze.Core.Application.Identity.xml
@@ -68,7 +68,7 @@
             
             </summary>
         </member>
-        <member name="P:Leuze.Core.Application.Identity.ApplicationRole.UserRoles">
+        <member name="P:Leuze.Core.Application.Identity.ApplicationRole.Users">
             <summary>
             
             </summary>
@@ -90,6 +90,12 @@
             
             </summary>
         </member>
+        <member name="M:Leuze.Core.Application.Identity.ApplicationRole.ChangeName(System.String)">
+            <summary>
+            
+            </summary>
+            <param name="name"></param>
+        </member>
         <member name="T:Leuze.Core.Application.Identity.ApplicationRoleClaim">
             <summary>
             
@@ -105,14 +111,20 @@
             
             </summary>
         </member>
-        <member name="M:Leuze.Core.Application.Identity.ApplicationUser.#ctor(System.String,System.String)">
+        <member name="M:Leuze.Core.Application.Identity.ApplicationUser.#ctor(System.String,System.String,System.Boolean)">
             <summary>
             
             </summary>
             <param name="userName"></param>
             <param name="email"></param>
+            <param name="isAdUser"></param>
         </member>
-        <member name="P:Leuze.Core.Application.Identity.ApplicationUser.UserRoles">
+        <member name="P:Leuze.Core.Application.Identity.ApplicationUser.IsAdUser">
+            <summary>
+            
+            </summary>
+        </member>
+        <member name="P:Leuze.Core.Application.Identity.ApplicationUser.Roles">
             <summary>
             
             </summary>
@@ -147,5 +159,25 @@
             
             </summary>
         </member>
+        <member name="T:Leuze.Core.Application.Identity.IApplicationUserProvider">
+            <summary>
+                An application service that provides currently authenticated user from presentation layer such as ASP.NET application or Console App.
+            </summary>
+        </member>
+        <member name="P:Leuze.Core.Application.Identity.IApplicationUserProvider.UserId">
+            <summary>
+                The current user identifier.
+            </summary>
+        </member>
+        <member name="P:Leuze.Core.Application.Identity.IApplicationUserProvider.User">
+            <summary>
+                The current user ClaimsPrincipal.
+            </summary>
+        </member>
+        <member name="P:Leuze.Core.Application.Identity.IApplicationUserProvider.IsAuthenticated">
+            <summary>
+                Checks whether the user is authenticated.
+            </summary>
+        </member>
     </members>
 </doc>
diff --git a/src/Core/Application/Leuze.Core.Application.Identity/ApplicationRole.cs b/src/Core/Application/Leuze.Core.Application.Identity/ApplicationRole.cs
index 4a41a4c..de74ff3 100644
--- a/src/Core/Application/Leuze.Core.Application.Identity/ApplicationRole.cs
+++ b/src/Core/Application/Leuze.Core.Application.Identity/ApplicationRole.cs
@@ -21,31 +21,41 @@ namespace Leuze.Core.Application.Identity
         /// <param name="name"></param>
         public ApplicationRole(string name) => (Name, NormalizedName) = (name, name.ToUpper());
 
+        private List<ApplicationPermission> _permissions = new List<ApplicationPermission>();
+
         /// <summary>
         /// 
         /// </summary>
-        public ICollection<ApplicationPermission> Permissions { get; private set; } = new List<ApplicationPermission>();
+        public IReadOnlyCollection<ApplicationPermission> Permissions => _permissions.AsReadOnly();
+
+        private List<ApplicationUser> _users = new List<ApplicationUser>();
 
         /// <summary>
         /// 
         /// </summary>
-        public ICollection<ApplicationUserRole> UserRoles { get; private set; } = new List<ApplicationUserRole>();
+        public IReadOnlyCollection<ApplicationUser> Users => _users.AsReadOnly();
 
         /// <summary>
         /// 
         /// </summary>
         /// <param name="permission"></param>
-        public void AssignPermission(ApplicationPermission permission) => Permissions.Add(permission);
+        public void AssignPermission(ApplicationPermission permission) => _permissions.Add(permission);
 
         /// <summary>
         /// 
         /// </summary>
         /// <param name="permission"></param>
-        public void RemovePermission(ApplicationPermission permission) => Permissions.Remove(permission);
+        public void RemovePermission(ApplicationPermission permission) => _permissions.Remove(permission);
 
         /// <summary>
         /// 
         /// </summary>
-        public void ClearPermissions() => Permissions.Clear();
+        public void ClearPermissions() => _permissions.Clear();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="name"></param>
+        public void ChangeName(string name) => (Name, NormalizedName) = (name, name.ToUpper());
     }
 }
diff --git a/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUser.cs b/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUser.cs
index cadddad..f71870d 100644
--- a/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUser.cs
+++ b/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUser.cs
@@ -20,12 +20,21 @@ namespace Leuze.Core.Application.Identity
         /// </summary>
         /// <param name="userName"></param>
         /// <param name="email"></param>
-        public ApplicationUser(string userName, string email) => (UserName, Email) = (userName, email);
+        /// <param name="isAdUser"></param>
+        public ApplicationUser(string userName, string email, bool isAdUser)
+            => (UserName, Email, IsAdUser) = (userName, email, isAdUser);
 
         /// <summary>
         /// 
         /// </summary>
-        public ICollection<ApplicationUserRole> UserRoles { get; private set; } = new List<ApplicationUserRole>();
+        public bool IsAdUser { get; private set; }
+
+        private List<ApplicationRole> _roles = new List<ApplicationRole>();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public IReadOnlyCollection<ApplicationRole> Roles => _roles.AsReadOnly();
 
     }
 }
diff --git a/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUserRole.cs b/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUserRole.cs
index 5237c3b..b7aff8a 100644
--- a/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUserRole.cs
+++ b/src/Core/Application/Leuze.Core.Application.Identity/ApplicationUserRole.cs
@@ -6,17 +6,17 @@ namespace Leuze.Core.Application.Identity
     /// <summary>
     /// 
     /// </summary>
-    public sealed class ApplicationUserRole : IdentityUserRole<Guid>
+    public class ApplicationUserRole : IdentityUserRole<Guid>
     {
         /// <summary>
         /// 
         /// </summary>
-        public ApplicationUser User { get; set; } = null!;
+        public virtual ApplicationUser User { get; set; } = null!;
 
         /// <summary>
         /// 
         /// </summary>
-        public ApplicationRole Role { get; set; } = null!;
+        public virtual ApplicationRole Role { get; set; } = null!;
 
     }
 }
diff --git a/src/Core/Application/Leuze.Core.Application.Identity/IApplicationUserProvider.cs b/src/Core/Application/Leuze.Core.Application.Identity/IApplicationUserProvider.cs
new file mode 100644
index 0000000..b6fd352
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application.Identity/IApplicationUserProvider.cs
@@ -0,0 +1,25 @@
+﻿using System.Security.Claims;
+
+namespace Leuze.Core.Application.Identity
+{
+    /// <summary>
+    ///     An application service that provides currently authenticated user from presentation layer such as ASP.NET application or Console App.
+    /// </summary>
+    public interface IApplicationUserProvider
+    {
+        /// <summary>
+        ///     The current user identifier.
+        /// </summary>
+        string? UserId { get; }
+
+        /// <summary>
+        ///     The current user ClaimsPrincipal.
+        /// </summary>
+        public ClaimsPrincipal? User { get; }
+
+        /// <summary>
+        ///     Checks whether the user is authenticated.
+        /// </summary>
+        bool IsAuthenticated { get; }
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application.UI/Leuze.Core.Application.UI.csproj b/src/Core/Application/Leuze.Core.Application.UI/Leuze.Core.Application.UI.csproj
index a1b6df7..cd5fc2e 100644
--- a/src/Core/Application/Leuze.Core.Application.UI/Leuze.Core.Application.UI.csproj
+++ b/src/Core/Application/Leuze.Core.Application.UI/Leuze.Core.Application.UI.csproj
@@ -23,6 +23,7 @@
 
   <ItemGroup>
     <PackageReference Include="BlazorStyled" Version="3.1.0" />
+    <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Core/Application/Leuze.Core.Application.UI/Pages/Counter.razor b/src/Core/Application/Leuze.Core.Application.UI/Pages/Counter.razor
deleted file mode 100644
index 8641f78..0000000
--- a/src/Core/Application/Leuze.Core.Application.UI/Pages/Counter.razor
+++ /dev/null
@@ -1,16 +0,0 @@
-﻿@page "/counter"
-
-<h1>Counter</h1>
-
-<p>Current count: @currentCount</p>
-
-<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
-
-@code {
-    private int currentCount = 0;
-
-    private void IncrementCount()
-    {
-        currentCount++;
-    }
-}
diff --git a/src/Core/Application/Leuze.Core.Application.UI/Pages/Index.razor b/src/Core/Application/Leuze.Core.Application.UI/Pages/Index.razor
index 138901d..253ce71 100644
--- a/src/Core/Application/Leuze.Core.Application.UI/Pages/Index.razor
+++ b/src/Core/Application/Leuze.Core.Application.UI/Pages/Index.razor
@@ -1,5 +1,6 @@
 ﻿@page "/"
 @layout MainLayout
+@attribute [Authorize]
 
 <h1>Hello, world!</h1>
 
diff --git a/src/Core/Application/Leuze.Core.Application.UI/Shared/MainLayout.razor b/src/Core/Application/Leuze.Core.Application.UI/Shared/MainLayout.razor
index f8dfacb..f45a40e 100644
--- a/src/Core/Application/Leuze.Core.Application.UI/Shared/MainLayout.razor
+++ b/src/Core/Application/Leuze.Core.Application.UI/Shared/MainLayout.razor
@@ -1,8 +1,14 @@
 ﻿@inherits LayoutComponentBase
 @inject NavigationManager _navManager
+@inject AuthenticationStateProvider AuthenticationStateProvider
 <div class="top-row px-4">
-    <img src="Resources/Icons/logo.svg" alt="logo" style="width:111px;height:28px;"/>
-    <img src="Resources/Icons/sign-out.svg" style="width:18px;height:20px;"/>
+    <img src="Resources/Icons/logo.svg" alt="logo" style="width:111px;height:28px;" />
+    <div>
+        @Name
+        <a href="/MicrosoftIdentity/Account/SignOut">
+            <img src="Resources/Icons/sign-out.svg" style="width:18px;height:20px;" />
+        </a>
+    </div>
 </div>
 
 
@@ -11,18 +17,21 @@
         <NavMenu />
     </div>
 
-        <div class="main">
-            <div class="content px-4">
-                @Body
-            </div>
+    <div class="main">
+        <div class="content px-4">
+            @Body
         </div>
     </div>
+</div>
+
+@code {        
 
-@code {
+    private string Name { get; set; } = string.Empty;
 
-    /*protected override void OnAfterRender(bool firstRender)
+    protected override async Task OnInitializedAsync()
     {
-        base.OnAfterRender(firstRender);
-        if (firstRender) _navManager.NavigateTo("account/login");
-    }*/
+        base.OnInitialized();
+        var identity = await AuthenticationStateProvider.GetAuthenticationStateAsync();
+        Name = identity.User.Identity!.Name!;
+    }
 }
diff --git a/src/Core/Application/Leuze.Core.Application/Authentication/ApplicationUserProvider.cs b/src/Core/Application/Leuze.Core.Application/Authentication/ApplicationUserProvider.cs
new file mode 100644
index 0000000..2c02b90
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Authentication/ApplicationUserProvider.cs
@@ -0,0 +1,35 @@
+﻿using Leuze.Core.Application.Identity;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.Authentication
+{
+    /// <inheritdoc />
+    public class ApplicationUserProvider : IApplicationUserProvider
+    {
+        /// <summary>
+        ///     A public constructor with required http context accessor to get user claims.
+        /// </summary>
+        /// <param name="httpContextAccessor">The HTTP Context accessor.</param>
+        public ApplicationUserProvider(IHttpContextAccessor httpContextAccessor)
+        {
+            UserId = httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
+            User = httpContextAccessor.HttpContext?.User;
+            IsAuthenticated = UserId != null;
+        }
+
+        /// <inheritdoc />
+        public string? UserId { get; }
+
+        /// <inheritdoc />
+        public ClaimsPrincipal? User { get; }
+
+        /// <inheritdoc />
+        public bool IsAuthenticated { get; }
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Authentication/DomainUserProvider.cs b/src/Core/Application/Leuze.Core.Application/Authentication/DomainUserProvider.cs
index 0b18ca7..27fc0ef 100644
--- a/src/Core/Application/Leuze.Core.Application/Authentication/DomainUserProvider.cs
+++ b/src/Core/Application/Leuze.Core.Application/Authentication/DomainUserProvider.cs
@@ -1,15 +1,27 @@
-﻿namespace Leuze.Core.Application.Authentication
+﻿using Leuze.Core.Application.Identity;
+using Leuze.Core.Domain.Domains.Users;
+using Leuze.Core.Domain.Domains.Users.Services.Abstractions;
+using Leuze.Core.Domain.Repositories;
+using System;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.Authentication
 {
     /// <inheritdoc />
-    public class DomainUserProvider// : IDomainUserProvider
+    public class DomainUserProvider : IDomainUserProvider
     {
-        /*private readonly IApplicationUserProvider _userProvider;
-        private readonly IStorage _unitOfWork;
+        private readonly IApplicationUserProvider _userProvider;
+        private readonly IDomainUserRepository _domainUserRepository;
 
-        public DomainUserProvider(IApplicationUserProvider userProvider, IStorage unitOfWork)
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="userProvider"></param>
+        /// <param name="domainUserRepository"></param>
+        public DomainUserProvider(IApplicationUserProvider userProvider, IDomainUserRepository domainUserRepository)
         {
             _userProvider = userProvider;
-            _unitOfWork = unitOfWork;
+            _domainUserRepository = domainUserRepository;
         }
 
         /// <inheritdoc />
@@ -21,9 +33,21 @@
             if (CurrentUserId is null)
                 return null;
 
-            return null;
-            //TODO: Implement
-            //return await _unitOfWork.DomainUsers.FindAsync(CurrentUserId);
+            return await _domainUserRepository.GetOptionalByIdAsync(CurrentUserId);
+        }
+
+        /// <inheritdoc />
+        public async Task<DomainUser> GetRequiredCurrentUserAsync()
+        {
+            if (CurrentUserId is null)
+                throw new UnauthorizedAccessException();
+
+            var user = await _domainUserRepository.GetByIdAsync(CurrentUserId.Value);
+
+            if (user is null)
+                throw new UnauthorizedAccessException();
+
+            return user;
         }
 
         /// <summary>
@@ -39,6 +63,6 @@
                 return userId;
 
             return null;
-        }*/
+        }
     }
 }
diff --git a/src/Core/Application/Leuze.Core.Application/Authentication/IApplicationUserProvider.cs b/src/Core/Application/Leuze.Core.Application/Authentication/IApplicationUserProvider.cs
deleted file mode 100644
index dfcfeac..0000000
--- a/src/Core/Application/Leuze.Core.Application/Authentication/IApplicationUserProvider.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-﻿namespace Leuze.Core.Application.Authentication
-{
-    /// <summary>
-    ///     An application service that provides currently authenticated user from presentation layer such as ASP.NET application or Console App.
-    /// </summary>
-    public interface IApplicationUserProvider
-    {
-        /*/// <summary>
-        ///     The current user identifier.
-        /// </summary>
-        string? UserId { get; }
-
-        /// <summary>
-        ///     The current user ClaimsPrincipal.
-        /// </summary>
-        public ClaimsPrincipal? User { get; }
-
-        /// <summary>
-        ///     Checks whether the user is authenticated.
-        /// </summary>
-        bool IsAuthenticated { get; }*/
-    }
-}
diff --git a/src/Core/Application/Leuze.Core.Application/Authorization/PermissionAuthorizationHandler.cs b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionAuthorizationHandler.cs
new file mode 100644
index 0000000..52dd9f7
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionAuthorizationHandler.cs
@@ -0,0 +1,47 @@
+﻿using Leuze.Core.Application.Identity;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.Authorization
+{
+    /// <summary>
+    /// Permission-based authorization handler.
+    /// </summary>
+    public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
+    {
+        private readonly UserManager<ApplicationUser> _userManager;
+        private readonly PermissionManager _permissionManager;
+
+        /// <summary>
+        /// Handler constructor with required dependencies.
+        /// </summary>
+        /// <param name="userManager">The user manager.</param>
+        /// <param name="permissionManager">The permission manager.</param>
+        public PermissionAuthorizationHandler(UserManager<ApplicationUser> userManager, PermissionManager permissionManager)
+            => (_userManager, _permissionManager) = (userManager, permissionManager);
+
+        /// <inheritdoc />
+        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
+        {
+            if (context.User.Identity is null || !context.User.Identity.IsAuthenticated)
+            {
+                context.Fail();
+                return;
+            }
+
+            // Get all the roles the user belongs to and check if any of the roles has the permission required
+            // for the authorization to succeed.
+            var user = await _userManager.GetUserAsync(context.User);
+            if (user is null)
+            {
+                context.Fail();
+                return;
+            }
+
+            var hasPermission = await _permissionManager.HasPermissionAsync(user.Id, requirement.PermissionName);
+            if (hasPermission) context.Succeed(requirement);
+            else context.Fail();
+        }
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Authorization/PermissionManager.cs b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionManager.cs
new file mode 100644
index 0000000..83de509
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionManager.cs
@@ -0,0 +1,31 @@
+﻿using Leuze.Core.Domain.Repositories;
+using System;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.Authorization
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class PermissionManager
+    {
+        private readonly IUserRepository _userRepository;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="userRepository"></param>
+        public PermissionManager(IUserRepository userRepository)
+            => _userRepository = userRepository;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="permission"></param>
+        /// <returns></returns>
+        public async Task<bool> HasPermissionAsync(Guid id, string permission)
+            => await _userRepository.HasPermissionAsync(id, permission);
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Authorization/PermissionPolicyProvider.cs b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionPolicyProvider.cs
new file mode 100644
index 0000000..8ea68a5
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionPolicyProvider.cs
@@ -0,0 +1,48 @@
+﻿using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.Authorization
+{
+    /// <summary>
+    ///     Authorization policy provider for permission based authorization.
+    /// <para>
+    ///     Provider creates policies dynamically when the permission policies are used.
+    /// </para>
+    /// </summary>
+    public class PermissionPolicyProvider : IAuthorizationPolicyProvider
+    {
+        /// <summary>
+        ///     Fallback authorization policy provider.
+        /// </summary>
+        public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
+
+        /// <summary>
+        ///     Creates an permission policy provider that creates policies dynamically based on permission in the application.
+        /// </summary>
+        /// <param name="options">Authorization configuration.</param>
+        public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
+            => FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
+
+        /// <inheritdoc />
+        public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
+
+        /// <inheritdoc />
+        public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => FallbackPolicyProvider.GetFallbackPolicyAsync();
+
+        /// <inheritdoc />
+        public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
+        {
+            if (policyName.StartsWith("PRIVILEGE.", StringComparison.OrdinalIgnoreCase))
+            {
+                var policy = new AuthorizationPolicyBuilder();
+                policy.AddRequirements(new PermissionRequirement(policyName.Replace("PRIVILEGE.", "")));
+                return Task.FromResult(policy.Build())!;
+            }
+
+            // Policy is not for permissions, try the default provider.
+            return FallbackPolicyProvider.GetPolicyAsync(policyName);
+        }
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Authorization/PermissionRequirement.cs b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionRequirement.cs
new file mode 100644
index 0000000..658dd33
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Authorization/PermissionRequirement.cs
@@ -0,0 +1,22 @@
+﻿using Microsoft.AspNetCore.Authorization;
+
+namespace Leuze.Core.Application.Authorization
+{
+    /// <summary>
+    ///     Authorization requirement for permissions in the system.
+    /// </summary>
+    public class PermissionRequirement : IAuthorizationRequirement
+    {
+        /// <summary>
+        ///     Initializes permission requirement with the permission name.
+        /// </summary>
+        /// <param name="permissionName">The permission name.</param>
+        public PermissionRequirement(string permissionName)
+            => PermissionName = permissionName;
+
+        /// <summary>
+        ///     The permission name.
+        /// </summary>
+        public string PermissionName { get; private set; }
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/CQRS/Auth/Commads/VerifyAdUser.cs b/src/Core/Application/Leuze.Core.Application/CQRS/Auth/Commads/VerifyAdUser.cs
new file mode 100644
index 0000000..8c0ef80
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/CQRS/Auth/Commads/VerifyAdUser.cs
@@ -0,0 +1,86 @@
+﻿using Leuze.Core.Application.Identity;
+using Leuze.Core.Domain.Domains.Users;
+using Leuze.Core.Domain.Repositories;
+using Microsoft.AspNetCore.Identity;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.CQRS.Auth.Commands
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class VerifyAdUser
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Command(string Email, string Name) : IBaseRequest<Response>;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Response(ApplicationUser User);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public class Handler : IBaseRequestHandler<Command, Response>
+        {
+            private readonly IUserRepository _userRepository;
+            private readonly IDomainUserRepository _domainUserRepository;
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="userRepository"></param>
+            /// <param name="domainUserRepository"></param>
+            /// <param name="userManager"></param>
+            public Handler(IUserRepository userRepository, IDomainUserRepository domainUserRepository)
+                => (_userRepository, _domainUserRepository) = (userRepository, domainUserRepository);
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="request"></param>
+            /// <param name="cancellationToken"></param>
+            /// <returns></returns>
+            public async Task<RequestResponse<Response>> Handle(Command request, CancellationToken cancellationToken)
+            {
+                var user = await _userRepository.GetAdByEmailAsync(request.Email);
+
+                if(user is not null)
+                {
+                    var domainUser = await _domainUserRepository.GetByIdAsync(user.Id);
+
+                    if (domainUser is null) return RequestResponse<Response>.CreateErrorResponse($"Domain user with ID '{user.Id}' not found");
+                    if(domainUser.Name.FullName != request.Name)
+                    {
+                        domainUser.ChangeName(new(request.Name));
+                        await _domainUserRepository.UpdateAsync(domainUser);
+                    }
+                    return RequestResponse<Response>.CreateSuccessResponse(new(user));
+                }
+
+                var canCreate = await _userRepository.CanCreateByEmailAsync(request.Email);
+                if(!canCreate) return RequestResponse<Response>.CreateErrorResponse($"User with email '{request.Email}' already exists");
+
+                user = new(request.Email, request.Email, true);
+                await _userRepository.AddAsync(user);
+
+                // Have to be loaded with Leuze DB context - otherwise want to create duplicate record in DB, but user must be created using UserManager (it's safe)
+                user = await _userRepository.GetAdByEmailAsync(request.Email);
+
+                DomainUser newDomainUser = new(user, new(request.Name), new(request.Email));
+                await _domainUserRepository.AddAsync(newDomainUser);
+
+                return RequestResponse<Response>.CreateSuccessResponse(new(user));
+            }
+        }
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/CQRS/Auth/Commads/VerifyLocalUser.cs b/src/Core/Application/Leuze.Core.Application/CQRS/Auth/Commads/VerifyLocalUser.cs
new file mode 100644
index 0000000..c64c260
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/CQRS/Auth/Commads/VerifyLocalUser.cs
@@ -0,0 +1,66 @@
+﻿using Leuze.Core.Application.Identity;
+using Microsoft.AspNetCore.Identity;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.CQRS.Auth.Commads
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class VerifyLocalUser
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Command(string Email, string Password) : IBaseRequest<Response>;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Response();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public class Handler : IBaseRequestHandler<Command, Response>
+        {
+            private readonly SignInManager<ApplicationUser> _signInManager;
+            private readonly UserManager<ApplicationUser> _userManager;
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="signInManager"></param>
+            /// <param name="userManager"></param>
+            public Handler(SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager)
+                => (_signInManager, _userManager) = (signInManager, userManager);
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="request"></param>
+            /// <param name="cancellationToken"></param>
+            /// <returns></returns>
+            public async Task<RequestResponse<Response>> Handle(Command request, CancellationToken cancellationToken)
+            {
+                var user = await _userManager.FindByEmailAsync(request.Email);
+                if (user is null)
+                    return RequestResponse<Response>.CreateErrorResponse($"Invalid email or password");
+
+                var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false);
+                if(!result.Succeeded)
+                    return RequestResponse<Response>.CreateErrorResponse($"Invalid email or password");
+
+                await _signInManager.SignInAsync(user, true);
+
+                return RequestResponse<Response>.CreateSuccessResponse(new());
+            }
+        }
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/CQRS/Users/Commands/CreateLocalUser.cs b/src/Core/Application/Leuze.Core.Application/CQRS/Users/Commands/CreateLocalUser.cs
new file mode 100644
index 0000000..5be7169
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/CQRS/Users/Commands/CreateLocalUser.cs
@@ -0,0 +1,96 @@
+﻿using Leuze.Core.Application.Configuration;
+using Leuze.Core.Application.Identity;
+using Leuze.Core.Domain.Domains.Users;
+using Leuze.Core.Domain.Repositories;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.UI.Services;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.CQRS.Users.Commands
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class CreateLocalUser
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Command(string Name, string Email, Guid? SeniorUserId, IList<Guid> Roles) : IBaseRequest<Response>;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Response();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public class Handler : IBaseRequestHandler<Command, Response>
+        {
+            private readonly IDomainUserRepository _domainUserRepository;
+            private readonly IUserRepository _userRepository;
+            private readonly UserManager<ApplicationUser> _userManager;
+            private readonly AppSettings _appSettings;
+            private readonly IHttpContextAccessor _httpContextAccessor;
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="domainUserRepository"></param>
+            /// <param name="userManager"></param>
+            /// <param name="emailSender"></param>
+            /// <param name="options"></param>
+            /// <param name="httpContextAccessor"></param>
+            /// <param name="userRepository"></param>
+            public Handler(IDomainUserRepository domainUserRepository, UserManager<ApplicationUser> userManager,
+                IOptions<AppSettings> options, IHttpContextAccessor httpContextAccessor, IUserRepository userRepository)
+                => (_domainUserRepository, _userManager, _appSettings, _httpContextAccessor, _userRepository)
+                        = (domainUserRepository, userManager, options.Value, httpContextAccessor, userRepository);
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="request"></param>
+            /// <param name="cancellationToken"></param>
+            /// <returns></returns>
+            public async Task<RequestResponse<Response>> Handle(Command request, CancellationToken cancellationToken)
+            {
+                var user = await _userManager.FindByEmailAsync(request.Email);
+                if (user is not null)
+                    return RequestResponse<Response>.CreateErrorResponse($"User with email '{request.Email}' already exists");
+
+                user = new ApplicationUser(request.Email, request.Email, false);
+                var result = await _userManager.CreateAsync(user);
+                if (!result.Succeeded)
+                    return RequestResponse<Response>.CreateErrorResponse($"User with username '{request.Email}' failed to create");
+
+                user = await _userRepository.GetByIdAsync(user.Id);
+
+                DomainUser? senior = null!;
+                if (request.SeniorUserId.HasValue) senior = await _domainUserRepository.GetOptionalByIdAsync(request.SeniorUserId);
+
+                DomainUser domainUser = new(user, new(request.Name), new(request.Email));
+
+                await _domainUserRepository.AddAsync(domainUser);
+
+                var resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
+
+                /*await _emailSender.SendAsync(new NewUserTemplate(
+                    null!,
+                    new MailAddress(domainUser.Email.GetEmailAddress()),
+                    "Vytvořen účet - Leuze",
+                    new() { ResetLink = $"{_httpContextAccessor.HttpContext!.Request.Scheme}://{_httpContextAccessor.HttpContext!.Request.Host}/{_appSettings.Authentication.ResetUrl}/?token={resetToken}&user={user.Email}" }
+                ));*/
+
+                return RequestResponse<Response>.CreateSuccessResponse(new());
+            }
+        }
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/CQRS/Users/Queries/GetFilteredUsers.cs b/src/Core/Application/Leuze.Core.Application/CQRS/Users/Queries/GetFilteredUsers.cs
new file mode 100644
index 0000000..3f1e471
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/CQRS/Users/Queries/GetFilteredUsers.cs
@@ -0,0 +1,55 @@
+﻿using Leuze.Core.Domain.Domains.Users.DTOs;
+using Leuze.Core.Domain.Repositories;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.CQRS.Users.Queries
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class GetFilteredUsers
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Query(int PageNumber, int PageSize) : IBaseRequest<Response>;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public record Response(List<UserDto> List, int Total);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public class Handler : IBaseRequestHandler<Query, Response>
+        {
+            private readonly IDomainUserRepository _domainUserRepository;
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="domainUserRepository"></param>
+            public Handler(IDomainUserRepository domainUserRepository)
+                => (_domainUserRepository) = (domainUserRepository);
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="request"></param>
+            /// <param name="cancellationToken"></param>
+            /// <returns></returns>
+            public async Task<RequestResponse<Response>> Handle(Query request, CancellationToken cancellationToken)
+            {
+                var result = await _domainUserRepository.GetFilteredListAsync(request.PageNumber, request.PageSize);
+                return RequestResponse<Response>.CreateSuccessResponse(new(result.List, result.Total));
+            }
+        }
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Configuration/Actions.cs b/src/Core/Application/Leuze.Core.Application/Configuration/Actions.cs
index 0ec5f8b..e95d45c 100644
--- a/src/Core/Application/Leuze.Core.Application/Configuration/Actions.cs
+++ b/src/Core/Application/Leuze.Core.Application/Configuration/Actions.cs
@@ -16,6 +16,16 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc.Authorization;
 using Microsoft.Identity.Web.UI;
+using System.Threading.Tasks;
+using System.Security.Claims;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Http;
+using Leuze.Core.Application.Identity;
+using Microsoft.AspNetCore.Identity;
+using Leuze.Core.Domain.Repositories;
+using Leuze.Core.Application.CQRS.Auth.Commands;
+using Leuze.Core.Application.Configuration.Extensions;
 
 namespace Leuze.Core.Application.Configuration
 {
@@ -51,7 +61,6 @@ namespace Leuze.Core.Application.Configuration
 
             applicationBuilder.UseHttpsRedirection();
             applicationBuilder.UseStaticFiles();
-            applicationBuilder.UseCookiePolicy();
             applicationBuilder.UseRouting();
             applicationBuilder.UseAuthentication();
             applicationBuilder.UseAuthorization();
@@ -94,13 +103,11 @@ namespace Leuze.Core.Application.Configuration
             appSettingsSection.Bind(appSettings);
             services.Configure<AppSettings>(appSettingsSection);
 
-            services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
-                .AddMicrosoftIdentityWebApp(configuration.GetSection("AzureAd"));
+            services.AddAzureAdAuthentication(configuration);
             services.AddControllersWithViews()
                 .AddMicrosoftIdentityUI();
 
-            // Options with default policy causes infinite redirect
-            services.AddAuthorization();
+            services.AddCustomAuthorization();
             services.AddRazorPages();
             services.AddServerSideBlazor()
                 .AddMicrosoftIdentityConsentHandler();
diff --git a/src/Core/Application/Leuze.Core.Application/Configuration/Extensions/CustomAuthenticationExtensions.cs b/src/Core/Application/Leuze.Core.Application/Configuration/Extensions/CustomAuthenticationExtensions.cs
new file mode 100644
index 0000000..7696012
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Configuration/Extensions/CustomAuthenticationExtensions.cs
@@ -0,0 +1,73 @@
+﻿using Leuze.Core.Application.Authentication;
+using Leuze.Core.Application.CQRS.Auth.Commands;
+using Leuze.Core.Application.Identity;
+using Leuze.Core.Domain.Domains.Users.Services.Abstractions;
+using MediatR;
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Identity.Web;
+using Serilog;
+using System;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Application.Configuration.Extensions
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class CustomAuthenticationExtensions
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="services"></param>
+        /// <param name="configuration"></param>
+        public static void AddAzureAdAuthentication(this IServiceCollection services, IConfiguration configuration)
+        {
+            services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+                .AddMicrosoftIdentityWebApp(options =>
+                {
+                    configuration.Bind("AzureAd", options);
+                    options.Events = new OpenIdConnectEvents
+                    {
+                        OnRedirectToIdentityProvider = async ctxt =>
+                        {
+                            await Task.Yield();
+                        },
+                        OnAuthenticationFailed = async ctxt =>
+                        {
+                            await Task.Yield();
+                        },
+                        OnTicketReceived = async ctxt =>
+                        {
+                            if (ctxt.Principal?.Identity is ClaimsIdentity identity)
+                            {
+                                var manager = ctxt.HttpContext.RequestServices.GetRequiredService<SignInManager<ApplicationUser>>();
+                                var mediator = ctxt.HttpContext.RequestServices.GetRequiredService<IMediator>();
+
+                                var result = await mediator.Send(new VerifyAdUser.Command(identity.Name!, identity.Claims.Single(o => o.Type == "name").Value));
+
+                                if (result.IsSuccess && result.Result is not null)
+                                    await manager.SignInAsync(result.Result.User, true);
+                            }
+                            await Task.Yield();
+                        },
+                        OnRedirectToIdentityProviderForSignOut = async ctxt =>
+                        {
+                            var manager = ctxt.HttpContext.RequestServices.GetRequiredService<SignInManager<ApplicationUser>>();
+                            await manager.SignOutAsync();
+                            await Task.Yield();
+                        }
+                    };
+                });
+
+            services.AddScoped<IDomainUserProvider, DomainUserProvider>();
+            services.AddScoped<IApplicationUserProvider, ApplicationUserProvider>();
+        }
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Configuration/Extensions/CustomAuthorizationExtensions.cs b/src/Core/Application/Leuze.Core.Application/Configuration/Extensions/CustomAuthorizationExtensions.cs
new file mode 100644
index 0000000..991abb1
--- /dev/null
+++ b/src/Core/Application/Leuze.Core.Application/Configuration/Extensions/CustomAuthorizationExtensions.cs
@@ -0,0 +1,27 @@
+﻿using Leuze.Core.Application.Authorization;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Leuze.Core.Application.Configuration.Extensions
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class CustomAuthorizationExtensions
+    {
+
+        /// <summary>
+        ///     Configures authorization policies.
+        /// </summary>
+        /// <param name="services">The service collection.</param>
+        public static void AddCustomAuthorization(this IServiceCollection services)
+        {
+            services.AddAuthorization();
+
+            services.AddTransient<PermissionManager>();
+            services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
+            services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
+        }
+
+    }
+}
diff --git a/src/Core/Application/Leuze.Core.Application/Leuze.Core.Application.csproj b/src/Core/Application/Leuze.Core.Application/Leuze.Core.Application.csproj
index 5157b8f..5d2e8d6 100644
--- a/src/Core/Application/Leuze.Core.Application/Leuze.Core.Application.csproj
+++ b/src/Core/Application/Leuze.Core.Application/Leuze.Core.Application.csproj
@@ -19,7 +19,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <Folder Include="Authorization\" />
     <Folder Include="CQRS\Roles\Queries\" />
   </ItemGroup>
 
@@ -38,6 +37,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.5" />
     <PackageReference Include="Microsoft.Identity.Web" Version="1.8.2" />
     <PackageReference Include="Microsoft.Identity.Web.UI" Version="1.8.2" />
+    <PackageReference Include="Serilog" Version="2.10.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Core/Domain/Leuze.Core.Domain/Domains/Shared/ValueObjects/PersonFullName.cs b/src/Core/Domain/Leuze.Core.Domain/Domains/Shared/ValueObjects/PersonFullName.cs
index 625dff0..9ecba65 100644
--- a/src/Core/Domain/Leuze.Core.Domain/Domains/Shared/ValueObjects/PersonFullName.cs
+++ b/src/Core/Domain/Leuze.Core.Domain/Domains/Shared/ValueObjects/PersonFullName.cs
@@ -13,25 +13,19 @@
         /// <summary>
         ///     Creates a <see cref="PersonFullName"/> with required properties.
         /// </summary>
-        /// <param name="firstName">The first name.</param>
-        /// <param name="lastName">The last name.</param>
-        public PersonFullName(string firstName, string lastName) => (FirstName, LastName) = (firstName, lastName);
+        /// <param name="fullName">The full name.</param>
+        public PersonFullName(string fullName) => (FullName) = (fullName);
 
         /// <summary>
-        ///     The first name.
+        ///     The full name.
         /// </summary>
-        public string FirstName { get; private set; } = null!;
-
-        /// <summary>
-        ///     The last name.
-        /// </summary>
-        public string LastName { get; private set; } = null!;
+        public string FullName { get; private set; } = null!;
 
         /// <summary>
         ///     Gets the full name of the person.
         /// </summary>
         /// <returns>The full name.</returns>
-        public override string ToString() => $"{FirstName} {LastName}";
+        public override string ToString() => $"{FullName}";
 
         /// <summary>
         ///     Gets a full name.
diff --git a/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DTOs/UserDto.cs b/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DTOs/UserDto.cs
new file mode 100644
index 0000000..d30d765
--- /dev/null
+++ b/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DTOs/UserDto.cs
@@ -0,0 +1,9 @@
+﻿using System;
+
+namespace Leuze.Core.Domain.Domains.Users.DTOs
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public record UserDto(Guid Id, string Name, string Email, bool IsAdUser);
+}
diff --git a/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DomainUser.cs b/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DomainUser.cs
index 7ecf29e..177e08d 100644
--- a/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DomainUser.cs
+++ b/src/Core/Domain/Leuze.Core.Domain/Domains/Users/DomainUser.cs
@@ -1,4 +1,5 @@
 ﻿using Leuze.Core.Application.Identity;
+using Leuze.Core.Domain.Domains.Shared.ValueObjects;
 using System;
 
 namespace Leuze.Core.Domain.Domains.Users
@@ -18,14 +19,14 @@ namespace Leuze.Core.Domain.Domains.Users
 
 #pragma warning restore 8618
 
-        private DomainUser(Guid userId) => (UserId) = (userId);
-
         /// <summary>
         /// 
         /// </summary>
-        /// <param name="userId"></param>
-        /// <returns></returns>
-        public static DomainUser NewUser(Guid userId) => new(userId);
+        /// <param name="user"></param>
+        /// <param name="name"></param>
+        /// <param name="email"></param>
+        public DomainUser(ApplicationUser user, PersonFullName name, EmailAddress email)
+            => (UserId, User, EmailAddress, Name) = (user.Id, user, email, name);
 
 
         /// <summary>
@@ -37,5 +38,21 @@ namespace Leuze.Core.Domain.Domains.Users
         /// 
         /// </summary>
         public ApplicationUser User { get; private set; } = null!;
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public PersonFullName Name { get; private set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public EmailAddress EmailAddress { get; private set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="name"></param>
+        public void ChangeName(PersonFullName name) => Name = name;
     }
 }
diff --git a/src/Core/Domain/Leuze.Core.Domain/Leuze.Core.Domain.csproj b/src/Core/Domain/Leuze.Core.Domain/Leuze.Core.Domain.csproj
index f6038e0..ba2e7f0 100644
--- a/src/Core/Domain/Leuze.Core.Domain/Leuze.Core.Domain.csproj
+++ b/src/Core/Domain/Leuze.Core.Domain/Leuze.Core.Domain.csproj
@@ -28,4 +28,8 @@
     <ProjectReference Include="..\..\Application\Leuze.Core.Application.Identity\Leuze.Core.Application.Identity.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Folder Include="Services\" />
+  </ItemGroup>
+
 </Project>
diff --git a/src/Core/Domain/Leuze.Core.Domain/Repositories/IDomainUserRepository.cs b/src/Core/Domain/Leuze.Core.Domain/Repositories/IDomainUserRepository.cs
new file mode 100644
index 0000000..45b1775
--- /dev/null
+++ b/src/Core/Domain/Leuze.Core.Domain/Repositories/IDomainUserRepository.cs
@@ -0,0 +1,51 @@
+﻿using ExtCore.Data.Abstractions;
+using Leuze.Core.Domain.Domains.Users;
+using Leuze.Core.Domain.Domains.Users.DTOs;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Domain.Repositories
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public interface IDomainUserRepository : IRepository
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        Task<DomainUser> GetByIdAsync(Guid id);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="domainUser"></param>
+        /// <returns></returns>
+        Task AddAsync(DomainUser domainUser);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="domainUser"></param>
+        /// <returns></returns>
+        Task UpdateAsync(DomainUser domainUser);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="pageNumber"></param>
+        /// <param name="pageSize"></param>
+        /// <returns></returns>
+        Task<(List<UserDto> List, int Total)> GetFilteredListAsync(int pageNumber, int pageSize);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="currentUserId"></param>
+        /// <returns></returns>
+        Task<DomainUser?> GetOptionalByIdAsync(Guid? currentUserId);
+    }
+}
diff --git a/src/Core/Domain/Leuze.Core.Domain/Repositories/IUserRepository.cs b/src/Core/Domain/Leuze.Core.Domain/Repositories/IUserRepository.cs
index 2471534..111fc32 100644
--- a/src/Core/Domain/Leuze.Core.Domain/Repositories/IUserRepository.cs
+++ b/src/Core/Domain/Leuze.Core.Domain/Repositories/IUserRepository.cs
@@ -1,4 +1,7 @@
 ﻿using ExtCore.Data.Abstractions;
+using Leuze.Core.Application.Identity;
+using System;
+using System.Threading.Tasks;
 
 namespace Leuze.Core.Domain.Repositories
 {
@@ -7,5 +10,40 @@ namespace Leuze.Core.Domain.Repositories
     /// </summary>
     public interface IUserRepository : IRepository
     {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="entity"></param>
+        /// <returns></returns>
+        Task AddAsync(ApplicationUser entity);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="email"></param>
+        /// <returns></returns>
+        Task<ApplicationUser> GetAdByEmailAsync(string email);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="email"></param>
+        /// <returns></returns>
+        Task<bool> CanCreateByEmailAsync(string email);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="permission"></param>
+        /// <returns></returns>
+        Task<bool> HasPermissionAsync(Guid id, string permission);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        Task<ApplicationUser> GetByIdAsync(Guid id);
     }
 }
diff --git a/src/Core/Domain/Leuze.Core.Domain/Services/IActiveDirectory.cs b/src/Core/Domain/Leuze.Core.Domain/Services/IActiveDirectory.cs
deleted file mode 100644
index 1fa4104..0000000
--- a/src/Core/Domain/Leuze.Core.Domain/Services/IActiveDirectory.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-﻿namespace Leuze.Core.Domain.Services
-{
-    /// <summary>
-    /// 
-    /// </summary>
-    public interface IActiveDirectory
-    {
-    }
-}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/DependencyInjection.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/DependencyInjection.cs
deleted file mode 100644
index e5a5040..0000000
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/DependencyInjection.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-﻿using ExtCore.Infrastructure.Actions;
-using Leuze.Core.Domain.Services;
-using Leuze.Core.Infrastructure.ActiveDirectory.Services;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.DependencyInjection;
-using System;
-
-namespace Leuze.Core.Infrastructure.ActiveDirectory
-{
-    /// <summary>
-    /// 
-    /// </summary>
-    public class ServicesConfiguration : IConfigureServicesAction
-    {
-        /// <summary>
-        /// 
-        /// </summary>
-        public int Priority => 1000;
-
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="services"></param>
-        /// <param name="serviceProvider"></param>
-        public void Execute(IServiceCollection services, IServiceProvider serviceProvider)
-        {
-            services.AddTransient<IActiveDirectory, AdService>();
-        }
-    }
-
-    /// <summary>
-    /// 
-    /// </summary>
-    public class ApplicationConfiguration : IConfigureAction
-    {
-        /// <summary>
-        /// 
-        /// </summary>
-        public int Priority => 11000;
-
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="applicationBuilder"></param>
-        /// <param name="serviceProvider"></param>
-        public void Execute(IApplicationBuilder applicationBuilder, IServiceProvider serviceProvider)
-        {
-        }
-    }
-}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/Leuze.Core.Infrastructure.ActiveDirectory.csproj b/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/Leuze.Core.Infrastructure.ActiveDirectory.csproj
deleted file mode 100644
index 1df276a..0000000
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/Leuze.Core.Infrastructure.ActiveDirectory.csproj
+++ /dev/null
@@ -1,29 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <AssemblyName>Leuze.Core.Infrastructure.ActiveDirectory</AssemblyName>
-    <RootNamespace>Leuze.Core.Infrastructure.ActiveDirectory</RootNamespace>
-    <Product></Product>
-    <Description></Description>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
-    <Nullable>enable</Nullable>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-    <OutputPath>..\..\..\Presentation\Leuze.Modules\</OutputPath>
-    <DocumentationFile>..\..\..\..\docs\Leuze.Core.Infrastructure.ActiveDirectory.xml</DocumentationFile>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="ExtCore.Infrastructure" Version="6.0.0" />
-  </ItemGroup>
-  
-  <ItemGroup>
-    <ProjectReference Include="..\..\Domain\Leuze.Core.Domain\Leuze.Core.Domain.csproj" />
-  </ItemGroup>
-
-</Project>
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/Services/AdService.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/Services/AdService.cs
deleted file mode 100644
index da687b7..0000000
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.ActiveDirectory/Services/AdService.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-﻿using Leuze.Core.Domain.Services;
-
-namespace Leuze.Core.Infrastructure.ActiveDirectory.Services
-{
-    /// <summary>
-    /// 
-    /// </summary>
-    public class AdService : IActiveDirectory
-    {
-    }
-}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Extensions/RepositoriesExtension.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Extensions/RepositoriesExtension.cs
index 33a2faf..0824d61 100644
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Extensions/RepositoriesExtension.cs
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Extensions/RepositoriesExtension.cs
@@ -16,6 +16,7 @@ namespace Leuze.Core.Infrastructure.Persistence.Extensions
         public static void AddRepositories(this IServiceCollection services)
         {
             services.AddTransient<IUserRepository, UserRepository>();
+            services.AddTransient<IDomainUserRepository, DomainUserRepository>();
             services.AddTransient<IRoleRepository, RoleRepository>();
             services.AddTransient<IPermissionRepository, PermissionRepository>();
         }
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Interceptors/AuditInterceptor.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Interceptors/AuditInterceptor.cs
index dd6c6b0..a082b33 100644
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Interceptors/AuditInterceptor.cs
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Interceptors/AuditInterceptor.cs
@@ -1,4 +1,5 @@
 ﻿using Leuze.Core.Application.Authentication;
+using Leuze.Core.Application.Identity;
 using Leuze.Core.Domain.Domains;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore.Diagnostics;
@@ -41,10 +42,10 @@ namespace Leuze.Core.Infrastructure.Persistence.Interceptors
                 switch (entry.State)
                 {
                     case EntityState.Added:
-                        entry.Entity.AuditCreation(null/*_applicationUserProvider?.UserId*/);
+                        entry.Entity.AuditCreation(_applicationUserProvider?.UserId);
                         break;
                     case EntityState.Modified:
-                        entry.Entity.AuditModification(null/*_applicationUserProvider?.UserId*/);
+                        entry.Entity.AuditModification(_applicationUserProvider?.UserId);
                         break;
                 }
 
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationPermissionRegistrar.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationPermissionRegistrar.cs
index 7bdb146..2be244a 100644
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationPermissionRegistrar.cs
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationPermissionRegistrar.cs
@@ -17,9 +17,13 @@ namespace Leuze.Core.Infrastructure.Persistence.Registrars.Identity
         {
             modelbuilder.Entity<ApplicationPermission>(etb =>
             {
-                etb.Property(o => o.Id).ValueGeneratedOnAdd();
-                etb.HasMany(o => o.Roles).WithMany(o => o.Permissions).UsingEntity(o => o.ToTable("RolesAssignedPermissions"));
-                etb.ToTable("ApplicationPermissions");
+                etb.HasKey(e => e.Id);
+                etb.Property(e => e.Id).ValueGeneratedOnAdd();
+                etb.Property(e => e.Name).HasMaxLength(512).IsRequired();
+                etb.Property(e => e.Module).HasMaxLength(512).IsRequired();
+                etb.Property(e => e.Privilege).HasMaxLength(2048).IsRequired();
+                etb.HasMany(e => e.Roles).WithMany(e => e.Permissions).UsingEntity(e => e.ToTable("Leuze_Roles_Assigned_Permissions"));
+                etb.ToTable("Leuze_Application_Permissions");
             });
         }
     }
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationRoleRegistrar.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationRoleRegistrar.cs
index f9507c2..56050ba 100644
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationRoleRegistrar.cs
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationRoleRegistrar.cs
@@ -17,17 +17,13 @@ namespace Leuze.Core.Infrastructure.Persistence.Registrars.Identity
         {
             modelbuilder.Entity<ApplicationRole>(etb =>
             {
-                etb.HasMany(o => o.Permissions).WithMany(o => o.Roles).UsingEntity(o => o.ToTable("RolesAssignedPermissions"));
-            });
-            modelbuilder.Entity<ApplicationUserRole>(entity =>
-            {
-                entity.HasOne(e => e.Role)
-                .WithMany(e => e.UserRoles)
-                .HasForeignKey(e => e.RoleId);
+                etb.HasMany(o => o.Permissions).WithMany(o => o.Roles).UsingEntity(o => o.ToTable("Leuze_Roles_Assigned_Permissions"));
 
-                entity.HasOne(e => e.User)
-                .WithMany(e => e.UserRoles)
-                .HasForeignKey(e => e.UserId);
+                etb.HasMany(o => o.Users).WithMany(o => o.Roles).UsingEntity<ApplicationUserRole>(
+                    o => o.HasOne(e => e.User).WithMany().HasForeignKey(e => e.UserId),
+                    o => o.HasOne(e => e.Role).WithMany().HasForeignKey(e => e.RoleId)
+                );
+                etb.ToTable("Leuze_Roles");
             });
         }
     }
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationUserRegistrar.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationUserRegistrar.cs
new file mode 100644
index 0000000..1f361d4
--- /dev/null
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Identity/ApplicationUserRegistrar.cs
@@ -0,0 +1,38 @@
+﻿using ExtCore.Data.EntityFramework;
+using Leuze.Core.Application.Identity;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Infrastructure.Persistence.Registrars.Identity
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class ApplicationUserRegistrar : IEntityRegistrar
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="modelbuilder"></param>
+        public void RegisterEntities(ModelBuilder modelbuilder)
+        {
+            modelbuilder.Entity<ApplicationUser>(etb =>
+            {
+                etb.Property(e => e.IsAdUser).IsRequired();
+                etb.HasMany(o => o.Roles).WithMany(o => o.Users).UsingEntity<ApplicationUserRole>(
+                    o => o.HasOne(e => e.Role)
+                             .WithMany()
+                             .HasForeignKey(e => e.RoleId),
+                    o => o.HasOne(e => e.User)
+                             .WithMany()
+                             .HasForeignKey(e => e.UserId)
+                );
+                etb.ToTable("Leuze_Users");
+            });
+        }
+    }
+}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Shared/ValueObjects/EmailAddressConfiguration.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Shared/ValueObjects/EmailAddressConfiguration.cs
new file mode 100644
index 0000000..1f655f9
--- /dev/null
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Shared/ValueObjects/EmailAddressConfiguration.cs
@@ -0,0 +1,50 @@
+﻿using Leuze.Core.Domain.Domains.Shared.ValueObjects;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Infrastructure.Persistence.Registrars.Shared.ValueObjects
+{
+    /// <summary>
+    ///     Static methods for configuring <see cref="EmailAddress"/> value object.
+    /// </summary>
+    public static class EmailAddressConfiguration
+    {
+        /// <summary>
+        ///     Extension method that sets up owned type relationship for <see cref="EmailAddress"/>.
+        /// </summary>
+        /// <param name="builder">The entity type builder.</param>
+        /// <param name="navigationExpression">The navigation expression for the owned entity.</param>
+        /// <typeparam name="TEntity">The source entity.</typeparam>
+        public static void OwnsEmailAddress<TEntity>(this EntityTypeBuilder<TEntity> builder, Expression<Func<TEntity, EmailAddress>> navigationExpression) where TEntity : class
+            => builder.OwnsOne(navigationExpression, Configure);
+
+        /// <summary>
+        ///     Extension method that sets up owned type relationship inside of an owned entity for <see cref="EmailAddress"/>.
+        /// </summary>
+        /// <param name="builder">The owned navigation builder.</param>
+        /// <param name="navigationExpression">The navigation expression for the owned entity.</param>
+        /// <typeparam name="TEntity">The source entity.</typeparam>
+        /// <typeparam name="TValueObject">The nested owned type.</typeparam>
+        public static void OwnsEmailAddress<TEntity, TValueObject>(this OwnedNavigationBuilder<TEntity, TValueObject> builder, Expression<Func<TValueObject, EmailAddress>> navigationExpression)
+            where TEntity : class
+            where TValueObject : class
+            => builder.OwnsOne(navigationExpression, Configure);
+
+
+        /// <summary>
+        ///     Configures the <see cref="EmailAddress"/> by using <paramref name="builder"/>.
+        /// </summary>
+        /// <param name="builder">The navigation builder.</param>
+        /// <typeparam name="TSource">The source entity, where we configure the value object.</typeparam>
+        private static void Configure<TSource>(OwnedNavigationBuilder<TSource, EmailAddress> builder) where TSource : class
+        {
+            builder.Property(e => e.Email)
+                .HasMaxLength(512);
+        }
+    }
+}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Shared/ValueObjects/PersonFullNameConfiguration.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Shared/ValueObjects/PersonFullNameConfiguration.cs
new file mode 100644
index 0000000..6d5fd70
--- /dev/null
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Shared/ValueObjects/PersonFullNameConfiguration.cs
@@ -0,0 +1,50 @@
+﻿using Leuze.Core.Domain.Domains.Shared.ValueObjects;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Infrastructure.Persistence.Registrars.Shared.ValueObjects
+{
+    /// <summary>
+    ///     Static methods for configuring <see cref="PersonFullName"/> value object.
+    /// </summary>
+    public static class PersonFullNameConfiguration
+    {
+        /// <summary>
+        ///     Extension method that sets up owned type relationship for <see cref="PersonFullName"/>.
+        /// </summary>
+        /// <param name="builder">The entity type builder.</param>
+        /// <param name="navigationExpression">The navigation expression for the owned entity.</param>
+        /// <typeparam name="TEntity">The source entity.</typeparam>
+        public static void OwnsFullName<TEntity>(this EntityTypeBuilder<TEntity> builder, Expression<Func<TEntity, PersonFullName>> navigationExpression) where TEntity : class
+            => builder.OwnsOne(navigationExpression, Configure);
+
+        /// <summary>
+        ///     Extension method that sets up owned type relationship inside of an owned entity for <see cref="PersonFullName"/>.
+        /// </summary>
+        /// <param name="builder">The owned navigation builder.</param>
+        /// <param name="navigationExpression">The navigation expression for the owned entity.</param>
+        /// <typeparam name="TEntity">The source entity.</typeparam>
+        /// <typeparam name="TValueObject">The nested owned type.</typeparam>
+        public static void OwnsFullName<TEntity, TValueObject>(this OwnedNavigationBuilder<TEntity, TValueObject> builder, Expression<Func<TValueObject, PersonFullName>> navigationExpression)
+            where TEntity : class
+            where TValueObject : class
+            => builder.OwnsOne(navigationExpression, Configure);
+
+
+        /// <summary>
+        ///     Configures the <see cref="PersonFullName"/> by using <paramref name="builder"/>.
+        /// </summary>
+        /// <param name="builder">The navigation builder.</param>
+        /// <typeparam name="TSource">The source entity, where we configure the value object.</typeparam>
+        private static void Configure<TSource>(OwnedNavigationBuilder<TSource, PersonFullName> builder) where TSource : class
+        {
+            builder.Property(e => e.FullName)
+                .HasMaxLength(256);
+        }
+    }
+}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Users/DomainUserRegistrar.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Users/DomainUserRegistrar.cs
index 6fece90..f4963eb 100644
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Users/DomainUserRegistrar.cs
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Registrars/Users/DomainUserRegistrar.cs
@@ -1,5 +1,6 @@
 ﻿using ExtCore.Data.EntityFramework;
 using Leuze.Core.Domain.Domains.Users;
+using Leuze.Core.Infrastructure.Persistence.Registrars.Shared.ValueObjects;
 using Microsoft.EntityFrameworkCore;
 
 namespace Leuze.Core.Infrastructure.Persistence.Registrars.Users
@@ -18,8 +19,12 @@ namespace Leuze.Core.Infrastructure.Persistence.Registrars.Users
             modelbuilder.Entity<DomainUser>(etb =>
             {
                 etb.HasKey(e => e.UserId);
-                etb.Property(e => e.UserId).ValueGeneratedNever();
                 etb.HasOne(e => e.User).WithOne().HasForeignKey<DomainUser>(e => e.UserId);
+                etb.Property(e => e.UserId).ValueGeneratedNever();
+
+                etb.OwnsEmailAddress(e => e.EmailAddress);
+                etb.OwnsFullName(e => e.Name);
+                etb.ToTable("Leuze_Domain_Users");
             });
         }
     }
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/DomainUserRepository.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/DomainUserRepository.cs
new file mode 100644
index 0000000..36a07a3
--- /dev/null
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/DomainUserRepository.cs
@@ -0,0 +1,66 @@
+﻿using AutoMapper;
+using ExtCore.Data.Abstractions;
+using Leuze.Core.Domain.Domains.Users;
+using Leuze.Core.Domain.Domains.Users.DTOs;
+using Leuze.Core.Domain.Repositories;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Leuze.Core.Infrastructure.Persistence.Repository
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class DomainUserRepository : RepositoryBase<DomainUser>, IDomainUserRepository
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="mapper"></param>
+        public DomainUserRepository(IStorageContext context, IMapper mapper) : base(context, mapper) { }
+
+        /// <inheritdoc/>
+        public async Task AddAsync(DomainUser domainUser)
+        {
+            await dbSet.AddAsync(domainUser);
+            await storageContext.SaveChangesAsync();
+        }
+
+        /// <inheritdoc/>
+        public async Task<DomainUser> GetByIdAsync(Guid id)
+            => await dbSet.SingleOrDefaultAsync(o => o.UserId == id);
+
+        /// <inheritdoc/>
+        public async Task<DomainUser?> GetOptionalByIdAsync(Guid? ownerId)
+        {
+            if (!ownerId.HasValue) return null!;
+            return await dbSet.SingleOrDefaultAsync(o => o.UserId == ownerId.Value);
+        }
+
+        /// <inheritdoc/>
+        public async Task<(List<UserDto> List, int Total)> GetFilteredListAsync(int pageNumber, int pageSize)
+        {
+            var query = dbSet.Include(o => o.User).AsNoTracking().AsQueryable();
+
+            var total = await query.CountAsync();
+
+            var list = await query.Select(o => new UserDto(o.UserId, o.Name.FullName, o.EmailAddress.Email, o.User.IsAdUser))
+                    .Skip((pageNumber - 1) * pageSize)
+                    .Take(pageSize)
+                    .ToListAsync();
+
+            return (list, total);
+        }
+
+        /// <inheritdoc/>
+        public async Task UpdateAsync(DomainUser domainUser)
+        {
+            dbSet.Update(domainUser);
+            await storageContext.SaveChangesAsync();
+        }
+    }
+}
diff --git a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/UserRepository.cs b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/UserRepository.cs
index 4d66224..748887c 100644
--- a/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/UserRepository.cs
+++ b/src/Core/Infrastructure/Leuze.Core.Infrastructure.Persistence/Repository/UserRepository.cs
@@ -3,6 +3,7 @@ using ExtCore.Data.Abstractions;
 using Leuze.Core.Application.Identity;
 using Leuze.Core.Domain.Repositories;
 using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -29,5 +30,30 @@ namespace Leuze.Core.Infrastructure.Persistence.Repository
             IMapper mapper,
             UserManager<ApplicationUser> userManager
         ) : base(context, mapper) => (_userManager) = (userManager);
+
+        /// <inheritdoc/>
+        public async Task AddAsync(ApplicationUser entity)
+        {
+            await _userManager.CreateAsync(entity);
+        }
+
+        /// <inheritdoc/>
+        public async Task<bool> CanCreateByEmailAsync(string email)
+            => await dbSet.Where(o => o.NormalizedEmail == email.ToUpper()).CountAsync() == 0;
+
+        /// <inheritdoc/>
+        public async Task<ApplicationUser> GetAdByEmailAsync(string email)
+            => await dbSet.Where(o => o.NormalizedEmail == email.ToUpper() && o.IsAdUser).SingleOrDefaultAsync();
+
+        /// <inheritdoc/>
+        public async Task<ApplicationUser> GetByIdAsync(Guid id)
+            => await dbSet.Where(o => o.Id == id).SingleOrDefaultAsync();
+
+        /// <inheritdoc/>
+        public async Task<bool> HasPermissionAsync(Guid id, string permission)
+            => await storageContext.Set<ApplicationUser>()
+                .Include(o => o.Roles)
+                .ThenInclude(o => o.Permissions)
+                .Where(o => o.Id == id && o.Roles.Any(a => a.Permissions.Any(u => u.Privilege == permission))).CountAsync() > 0;
     }
 }
