Built-In Identity Auth Admin UI

Built-In Identity Auth Admin UI Background
3 min read

With ServiceStack now deeply integrated into ASP.NET Core Apps we're back to refocusing on adding value-added features that can benefit all .NET Core Apps.

Registration

The new Identity Auth Admin UI is an example of this, which can be enabled when registering the AuthFeature Plugin:

public class ConfigureAuth : IHostingStartup
{
    public void Configure(IWebHostBuilder builder) => builder
        .ConfigureServices(services => {
            services.AddPlugin(new AuthFeature(IdentityAuth.For<ApplicationUser>(
                options => {
                    options.SessionFactory = () => new CustomUserSession();
                    options.CredentialsAuth();
                    options.AdminUsersFeature();
                })));
        });
}

Which just like the ServiceStack Auth Admin Users UI enables a Admin UI that's only accessible to Admin Users for managing Identity Auth users at /admin-ui/users.

User Search Results

Which displays a limited view due to the minimal properties on the default IdentityAuth model:

Custom Search Result Properties

These User's search results are customizable by specifying the ApplicationUser properties to display instead, e.g:

options.AdminUsersFeature(feature =>
{
    feature.QueryIdentityUserProperties =
    [
        nameof(ApplicationUser.Id),
        nameof(ApplicationUser.DisplayName),
        nameof(ApplicationUser.Email),
        nameof(ApplicationUser.UserName),
        nameof(ApplicationUser.LockoutEnd),
    ];
});

Custom Search Result Behavior

The default display Order of Users is also customizable:

feature.DefaultOrderBy = nameof(ApplicationUser.DisplayName);

As well as the Search behavior which can be replaced to search any custom fields, e.g:

feature.SearchUsersFilter = (q, query) =>
{
    var queryUpper = query.ToUpper();
    return q.Where(x =>
        x.DisplayName!.Contains(query) ||
        x.Id.Contains(queryUpper) ||
        x.NormalizedUserName!.Contains(queryUpper) ||
        x.NormalizedEmail!.Contains(queryUpper));
};

Default Create and Edit Users Forms

The default Create and Edit Admin Users UI are also limited to editing the minimal IdentityAuth properties:

Whilst the Edit page includes standard features to lockout users, change user passwords and manage their roles:

Custom Create and Edit Forms

By default Users are locked out indefinitely, but this can also be changed to lock users out to a specific date, e.g:

feature.ResolveLockoutDate = user => DateTimeOffset.Now.AddDays(7);

The forms editable fields can also be customized to include additional properties, e.g:

feature.FormLayout =
[
    Input.For<ApplicationUser>(x => x.UserName, c => c.FieldsPerRow(2)),
    Input.For<ApplicationUser>(x => x.Email, c => { 
        c.Type = Input.Types.Email;
        c.FieldsPerRow(2); 
    }),
    Input.For<ApplicationUser>(x => x.FirstName, c => c.FieldsPerRow(2)),
    Input.For<ApplicationUser>(x => x.LastName, c => c.FieldsPerRow(2)),
    Input.For<ApplicationUser>(x => x.DisplayName, c => c.FieldsPerRow(2)),
    Input.For<ApplicationUser>(x => x.PhoneNumber, c =>
    {
        c.Type = Input.Types.Tel;
        c.FieldsPerRow(2); 
    }),
];

That can override the new ApplicationUser Model that's created and any Validation:

Custom User Creation

feature.CreateUser = () => new ApplicationUser { EmailConfirmed = true };
feature.CreateUserValidation = async (req, createUser) =>
{
    await IdentityAdminUsers.ValidateCreateUserAsync(req, createUser);
    var displayName = createUser.GetUserProperty(nameof(ApplicationUser.DisplayName));
    if (string.IsNullOrEmpty(displayName))
        throw new ArgumentNullException(nameof(AdminUserBase.DisplayName));
    return null;
};

Admin User Events

Should you need to, Admin User Events can use used to execute custom logic before and after creating, updating and deleting users, e.g:

feature.OnBeforeCreateUser = (request, user) => { ... };
feature.OnAfterCreateUser  = (request, user) => { ... };
feature.OnBeforeUpdateUser = (request, user) => { ... };
feature.OnAfterUpdateUser  = (request, user) => { ... };
feature.OnBeforeDeleteUser = (request, userId) => { ... };
feature.OnAfterDeleteUser  = (request, userId) => { ... };