Skip to content

andregeuze/blazor-identity-sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Blazor Server Identity Provider with ASP.NET Core Identity using OpenIddict

Based on official microsoft documentation for ASP.NET Core Identity.

In this sample we will be using:

Steps to run through this sample/tutorial

Create the solution file

dotnet new sln -n BlazorIdentity

Create and add a clean blazor server project

dotnet new blazorserver -n BlazorIdentity.Server
dotnet sln add BlazorIdentity.Server

Add dependencies to the project

Install the tools used in this sample:

dotnet tool install -g dotnet-ef
dotnet tool install -g dotnet-aspnet-codegenerator

Add the packages:

dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity.UI
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package OpenIddict.AspNetCore -v 3.0.0-beta5.20503.76
dotnet add package OpenIddict.EntityFrameworkCore -v 3.0.0-beta5.20503.76

Create the Database context

Create an ApplicationUser class in /Models:

public class ApplicationUser : IdentityUser
{
}

Create the DbContext class in /Data:

public class AppDbContext : IdentityDbContext<ApplicationUser>
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }
}

Setup other required classes

Create /Services/EmailSender.cs:

public class EmailSender : IEmailSender
{
    public Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        return Task.CompletedTask;
    }
}

Add to Startup.cs, ConfigureServices method:

services.AddDbContext<AppDbContext>(options =>
{
    // Configure the context to use SQLite.
    options.UseSqlite(Configuration.GetConnectionString("AppDbContextConnection"));

    // Register the entity sets needed by OpenIddict.
    // Note: use the generic overload if you need
    // to replace the default OpenIddict entities.
    options.UseOpenIddict();
});

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
    // Add more password requirements ...
})
    .AddEntityFrameworkStores<AppDbContext>()
    .AddDefaultTokenProviders();

// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
    options.ClaimsIdentity.UserNameClaimType = Claims.Name;
    options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
    options.ClaimsIdentity.RoleClaimType = Claims.Role;
});

services.AddOpenIddict()
    // Register the OpenIddict core components.
    .AddCore(options =>
    {
        // Configure OpenIddict to use the Entity Framework Core stores and models.
        // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
        options.UseEntityFrameworkCore()
                .UseDbContext<AppDbContext>();
    })

    // Register the OpenIddict server components.
    .AddServer(options =>
    {
        // Enable the authorization, logout, token and userinfo endpoints.
        options.SetAuthorizationEndpointUris("/connect/authorize")
                .SetLogoutEndpointUris("/connect/logout")
                .SetTokenEndpointUris("/connect/token")
                .SetUserinfoEndpointUris("/connect/userinfo");

        // Mark the "email", "profile" and "roles" scopes as supported scopes.
        options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles);

        // Note: the sample uses the code and refresh token flows but you can enable
        // the other flows if you need to support implicit, password or client credentials.
        options.AllowAuthorizationCodeFlow()
                .AllowRefreshTokenFlow();

        // Register the signing and encryption credentials.
        options.AddDevelopmentEncryptionCertificate()
                .AddDevelopmentSigningCertificate();

        // Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
        options.UseAspNetCore()
                .EnableAuthorizationEndpointPassthrough()
                .EnableLogoutEndpointPassthrough()
                .EnableStatusCodePagesIntegration()
                .EnableTokenEndpointPassthrough();
    })

    // Register the OpenIddict validation components.
    .AddValidation(options =>
    {
        // Import the configuration from the local OpenIddict server instance.
        options.UseLocalServer();

        // Register the ASP.NET Core host.
        options.UseAspNetCore();
    });

// Add our custom Email Sender
services.AddSingleton<IEmailSender, EmailSender>();

Add to Startup.cs, Configure method:

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapControllers();

    endpoints.MapBlazorHub(); // Enable For Blazor Server only
    endpoints.MapFallbackToPage("/_Host"); // Enable For Blazor Server only
});

Set up the database (Sqlite)

Create an initial migration and run it:

dotnet ef migrations add InitialSchema -o "Data/Migrations"
dotnet ef database update

Scaffold all the Identity files

Use for only these pages:

dotnet aspnet-codegenerator identity -dc BlazorIdentity.Server.Data.AppDbContext -sqlite --files "Account.Register;Account.Login;Account.Logout;Account.ResetPassword"

Use to scaffold ALL Identity pages:

dotnet aspnet-codegenerator identity -dc BlazorIdentity.Server.Data.AppDbContext -sqlite

Apply --force to regenerate.

First Test !

Open the url to check your OpenID specification:

https://localhost:5001/.well-known/openid-configuration

Let's continue!

Create a redirect component

Create component /Shared/RedirectToLogin.cs:

public class RedirectToLogin : ComponentBase
{
    [Inject] NavigationManager NavigationManager { get; set; }

    protected override void OnInitialized()
    {
        NavigationManager.NavigateTo($"Identity/Account/Login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}", true);
    }
}

Create a login display component in /Shared/LoginDisplay.razor:

@using Microsoft.AspNetCore.Components.Authorization @inject NavigationManager
Navigation

<AuthorizeView>
  <Authorized>
    <a href="Identity/Account/Manage/Index">
      Hello, @context.User.Identity.Name!
    </a>
    <form action="/Identity/Account/Logout?returnUrl=%2F" method="post">
      <button class="nav-link btn btn-link" type="submit">Logout</button>
    </form>
  </Authorized>
  <NotAuthorized>
    <a href="Identity/Account/Login">Login</a>
  </NotAuthorized>
</AuthorizeView>

Add the Login Display to MainLayout.razor:

<div class="top-row px-4">
  <LoginDisplay />
  <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>

About

A working sample of Blazor Server combined with ASP.NET Core Identity

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published