Introduction
BitzArt.Blazor.Auth is a Blazor library that provides tearless authentication and authorization capabilities for your Blazor applications. Thanks to Blazor.Cookies, it works with all Blazor United render modes:
Blazor Rendermode | Support |
---|---|
Static SSR | ✔️ |
Interactive Server | ✔️ |
Interactive WebAssembly | ✔️ |
Interactive Auto | ✔️ |
Getting Started
Refer to the Getting Started section to learn how to use this library in your Blazor applications.
Getting Started
Installation
Add this nuget package to your Server project:
dotnet add package BitzArt.Blazor.Auth.Server
Add this nuget package to your Client project:
dotnet add package BitzArt.Blazor.Auth.Client
Configuration
Call this method in both your Server Program.cs
and Client Program.cs
:
builder.AddBlazorAuth();
Usage
You can now use Blazor authorization capabilities (such as @attribute [Authorize]
, AuthorizeView
, AuthorizeRouteView
) as you normally would.
What's next?
Refer to the Authentication section to learn how to customize your authentication logic.
Authentication
💡 You can always refer to the sample project for additional guidance.
In order to implement User authentication with this library, you need to implement the IAuthenticationService
in your Blazor Server project and specify it when calling the AddBlazorAuth
method in your Blazor Server Program.cs
file.
ℹ️
IAuthenticationService
will be available in both your Server and Client projects, but you should only implement it in your Server project. The Client project will use the Server implementation out-of-the-box by calling the Server's API.
Implementation
Sign-In Payload
Create a class for your Sign-In payload. This class will be used to pass the user's credentials to the IAuthenticationService
implementation.
📜 This class needs to be serializable, so it can be passed between the Client and Server projects over http.
// Example Sign-In payload
public class SignInPayload
{
public string Email { get; set; }
public string Password { get; set; }
}
Sign-Up Payload
You don't need to do this if you don't want to implement the Sign-Up functionality.
Create a class for your Sign-Up payload. This class will be used to pass the user's credentials to the IAuthenticationService
implementation.
📜 This class needs to be serializable, so it can be passed between the Client and Server projects over http.
// Example Sign-Up payload
public class SignUpPayload
{
public string Email { get; set; }
public string Password { get; set; }
}
Authentication Service
You can inherit from the base ServerSideAuthenticationService
class in order to simplify the implementation of the IAuthenticationService
in your Blazor Server project.
// Example Authentication Service
public class SampleServerSideAuthenticationService
: ServerSideAuthenticationService<SignInPayload, SignUpPayload>
{
protected override Task<AuthenticationResult> GetSignInResultAsync(SignInPayload signInPayload)
{
var jwtPair = CreateSampleJwtPair(); // Replace this with your actual authentication logic
var authResult = AuthenticationResult.Success(jwtPair);
return Task.FromResult(authResult);
}
protected override Task<AuthenticationResult> GetSignUpResultAsync(SignUpPayload signUpPayload)
{
var jwtPair = CreateSampleJwtPair(); // Replace this with your actual authentication logic
var authResult = AuthenticationResult.Success(jwtPair);
return Task.FromResult(authResult);
}
public override Task<AuthenticationResult> RefreshJwtPairAsync(string refreshToken)
{
var jwtPair = CreateSampleJwtPair(); // Replace this with your actual authentication logic
var authResult = AuthenticationResult.Success(jwtPair);
return Task.FromResult(authResult);
}
private JwtPair CreateSampleJwtPair()
{
return new JwtPair
{
AccessToken = "my-access-token",
RefreshToken = "my-refresh-token"
};
}
}
Register the Authentication Service
Specify your IAuthenticationService
implementation when calling the AddBlazorAuth
method in your Blazor Server Program.cs
file.
// Program.cs
builder.AddBlazorAuth<SampleServerSideAuthenticationService>();
Add the authentication endpoints in your Blazor Server Program.cs
. This is required in order to allow the Client project to call the Server's API and use the server-side IAuthenticationService
implementation.
// Program.cs
app.MapAuthEndpoints();
User Service
You can now use the IUserService
in your Blazor Pages to Sign-In, Sign-Up, and Refresh the JWT Pair. The IUserService
will call your IAuthenticationService
implementation to perform these actions. Upon receiving a response, the IUserService
will then update the User's state. For additional guidance, see sample flows in Use Cases section.
ℹ️ You can also inject and use the
IAuthenticationService
directly in your Blazor application, it will process the Sign-In, Sign-Up, and Refresh Token requests without updating the User's state. You can inject theIAuthenticationService
in the wasm part of your app. It will call the Server's WebAPI to perform the authentication action and return the same response as if the action was performed on the server. Keep in mind that in this case a serialization/deserialization process will take place in order to pass the data between the Client and Server projects.
Token duration
You can specify the duration of your access token and refresh token whenever you are providing your JwtPair
to Blazor.Auth. The duration of the access token should normally be short, while the refresh token should normally be long-lived.
return new JwtPair
{
AccessToken = "my-access-token"
RefreshToken = "my-refresh-token"
AccessTokenExpiresAt = DateTimeOffset.UtcNow.AddMinutes(15),
RefreshTokenExpiresAt = DateTimeOffset.UtcNow.AddDays(7)
}
⚠️ Not providing an expiration date for the tokens will result in them being session-scoped. This means that the tokens will expire when the browser tab closes.
Sign out
You can sign the user out by calling the SignOutAsync
method of the IUserService
. This will clear the user's JwtPair.
await UserService.SignOutAsync();
NavigationManager.NavigateTo("/", true);
Use Cases
While your end soultion code regarding authentication remains the same, the way Blazor.Auth behaves under the hood may vary depending on your current render mode.
The following segments will guide you through the most common use-cases such as Sign-In, Sign-Up and Sign-Out and show you how to implement them in your Blazor application.
More detailed diagrams are organized according to different Blazor interactivity types:
Refresh Token
On every GetAuthenticationStateAsync
call to AuthenticationStateProvider
, it will check if the current access token is expired. If it is, the user's JWT Pair will automatically be refreshed.
For a more in-depth overview of this process, refer to the Architecture section.
Back to Use Cases
Static SSR
The following sequence diagrams illustrate the use cases for Blazor.Auth in a Blazor application that is currently using Static SSR
render mode.
For implementing these use-cases, an approach utilizing form submission is used. For more information on how Blazor handles form submission in Static SSR, refer to Blazor Documentation.
Sign-In
sequenceDiagram actor user as User participant page as SignIn Page box rgba(101, 63, 232, 0.5) Blazor.Auth participant userService as IUserService (Server) end participant authService as IAuthenticationService (Server) user ->>+ page: Submit Sign-In Form page ->>+ userService: SignInAsync(signInPayload) note over user,userService: Call IUserService's `SignInAsync` method when handling sign-in form submission. userService ->>+ authService: SignInAsync(signInPayload) authService ->> authService: Your server-side sign-in logic authService -->>- userService: AuthenticationResult userService -->> userService: Update authentication cookies userService -->>- page: AuthenticationResult page -->>- user: Form submission HTTP Response note over user,userService: Updated cookies will be attached to the HTTP response <br/> which will result in them being automalically stored in the user's browser.
Sign-Up
sequenceDiagram actor user as User participant page as SignUp Page box rgba(101, 63, 232, 0.5) Blazor.Auth participant userService as IUserService (Server) end participant authService as IAuthenticationService (Server) user ->>+ page: Submit Sign-Up Form page ->>+ userService: SignUpAsync(signUpPayload) note over user,userService: Call IUserService's `SignUpAsync` method when handling sign-up form submission. userService ->>+ authService: SignUpAsync(signUpPayload) authService ->> authService: Your server-side sign-up logic authService -->>- userService: AuthenticationResult userService -->> userService: Update authentication cookies userService -->>- page: AuthenticationResult page -->>- user: Form submission HTTP Response note over user,userService: Updated cookies will be attached to the HTTP response <br/> which will result in them being automalically stored in the user's browser.
Sign-Out
sequenceDiagram actor user as User participant page as Page box rgba(101, 63, 232, 0.5) Blazor.Auth participant userService as IUserService (Server) end user ->>+ page: Submit Sign-Out Form page ->>+ userService: SignOutAsync() note over user,userService: Call IUserService's `SignOutAsync` method when handling sign-out form submission. userService -->> userService: Remove authentication cookies userService -->>- page: page -->>- user: Form submission HTTP Response note over user,userService: Blazor.Auth will remove the user's authentication cookies <br/> by marking them as expired in the HTTP response.
Back to Use Cases
Interactive WebAssembly
The following sequence diagrams illustrate the use cases for Blazor.Auth in a Blazor application that is currently using Interactive WebAssembly
render mode:
Sign-In
sequenceDiagram actor user as User participant page as SignIn Page box rgba(101, 63, 232, 0.5) Blazor.Auth participant userServiceClient as IUserService (Client) participant userServiceServer as IUserService (Server) end participant authService as IAuthenticationService (Server) user ->>+ page: Submit Sign-In Form page ->>+ userServiceClient: SignInAsync(signInPayload) note over page,userServiceClient: Resolve IUserService from DI<br/> and call it's `SignInAsync` method. userServiceClient ->>+ userServiceServer: HTTP request to Sign-In endpoint note over userServiceClient,userServiceServer: Client-side implementation <br/> will make an HTTP request to the server, <br/> no manual action is required. userServiceServer ->>+ authService: SignInAsync(signInPayload) authService ->> authService: Your server-side sign-in logic authService -->>- userServiceServer: AuthenticationResult userServiceServer -->> userServiceClient: AuthenticationResult note over userServiceServer,userServiceClient: Server-side implementation will <br/> return AuthenticationResult to the client#59; <br/><br/> Updated cookies will be attached <br/> to the HTTP response <br/> which will result in them being <br/> automalically stored in the user's browser. userServiceClient -->>- page: AuthenticationResult page -->>- user: Continue, <br/> e.g. redirect to home page, <br/> or other custom logic note over user,userServiceClient: Upon receiving AuthenticationResult from IUserService, <br/> do a page refresh in order to refresh the User's AuthenticationState<br/><br/> Example: NavigationManager.NavigateTo("/", true).
Sign-Up
sequenceDiagram actor user as User participant page as SignUp Page box rgba(101, 63, 232, 0.5) Blazor.Auth participant userServiceClient as IUserService (Client) participant userServiceServer as IUserService (Server) end participant authService as IAuthenticationService (Server) user ->>+ page: Submit Sign-Up Form page ->>+ userServiceClient: SignUpAsync(signUpPayload) note over page,userServiceClient: Resolve IUserService from DI<br/> and call it's `SignUpAsync` method. userServiceClient ->>+ userServiceServer: HTTP request to Sign-Up endpoint note over userServiceClient,userServiceServer: Client-side implementation <br/> will make an HTTP request to the server, <br/> no manual action is required. userServiceServer ->>+ authService: SignUpAsync(signUpPayload) authService ->> authService: Your server-side sign-up logic authService -->>- userServiceServer: AuthenticationResult userServiceServer -->> userServiceClient: AuthenticationResult note over userServiceServer,userServiceClient: Server-side implementation will <br/> return AuthenticationResult to the client#59; <br/><br/> Updated cookies will be attached <br/> to the HTTP response <br/> which will result in them being <br/> automalically stored in the user's browser. userServiceClient -->>- page: AuthenticationResult page -->>- user: Continue, <br/> e.g. redirect to home page, <br/> or other custom logic note over user,userServiceClient: Upon receiving AuthenticationResult from IUserService, <br/> do a page refresh in order to refresh the User's AuthenticationState<br/><br/> Example: NavigationManager.NavigateTo("/", true).
Sign-Out
sequenceDiagram actor user as User participant page as Page box rgba(101, 63, 232, 0.5) Blazor.Auth participant userServiceClient as IUserService (Client) participant userServiceServer as IUserService (Server) end user ->>+ page: Initiate Sign-Out page ->>+ userServiceClient: SignOutAsync() note over page,userServiceClient: Resolve IUserService from DI<br/> and call it's `SignOutAsync` method. userServiceClient ->>+ userServiceServer: HTTP request to Sign-Out endpoint note over userServiceClient,userServiceServer: Client-side implementation will <br/> make an HTTP request to the server, <br/> no manual action is required. userServiceServer -->> userServiceClient: HTTP OK note over userServiceServer,userServiceClient: Blazor.Auth will remove <br/> the user's authentication cookies <br/> by marking them as expired <br/> in the HTTP response. userServiceClient -->>- page: #32; page -->>- user: Continue, <br/> e.g. redirect to home page, <br/> or other custom logic note over user,userServiceClient: Upon sign-out process completion, <br/> do a page refresh in order to refresh the User's AuthenticationState<br/><br/> Example: NavigationManager.NavigateTo("/", true).
Back to Use Cases
Interactive Server
The following sequence diagrams illustrate the use cases for Blazor.Auth in a Blazor application that is currently using Interactive Server
render mode:
Sign-In
🚧 This use-case is currently not supported for this render mode. Check back later.
Sign-Up
🚧 This use-case is currently not supported for this render mode. Check back later.
Sign-Out
🚧 This use-case is currently not supported for this render mode. Check back later.
Architecture
The package works by setting and reading Cookies in User's Browser. The Cookies are used to store the JWT Pair (Access Token and Refresh Token). The Access Token is used to authenticate the User in the Server, and the Refresh Token is used to get a new Access Token when the current one expires.
AuthenticationStateProvider implementations
InteractiveWebAssembly
sequenceDiagram participant client as Blazor Client participant provider as InteractiveWasmAuthenticationStateProvider participant server as Blazor Server client ->>+ provider: GetAuthenticationStateAsync provider ->>+ server: Http request: /auth-state server ->> server: if AccessToken expired: Refresh JwtPair server ->> server: Retrieve Claims from cookies server -->>- provider: Claims note over server,provider: Http response updates browser cookies provider ->> provider: Creates AuthenticationState <br/> from provided claims provider -->>- client: AuthenticationState
InteractiveServer
sequenceDiagram participant client as Blazor Client participant server as Blazor Server participant provider as InteractiveServerAuthenticationStateProvider server ->>+ provider: GetAuthenticationStateAsync provider ->> provider: Generate unique request identifier provider ->>+ server: Subscribe note over server,provider: Subscribe to an HTTP request from client using unique identifier server -->>- provider: provider ->>+ client: RequestClientSideHttpRequestAsync note over client,provider: Request Blazor Client to make an HTTP Request to Blazor Server via JS Interop client -->> provider: note over client: Un-awaited JS promise stays<br/>after JSInterop call is completed provider -->> provider: Wait until OnClientSideHttpRequest<br/>Invokation is complete client -)+ server: HTTP Request deactivate client server ->>+ provider: OnClientSideHttpRequest (id: uniqueIdentifier, request: HttpRequest) provider ->> provider: if AccessToken expired: Refresh JwtPair provider -->>- server: UpdatedCookies server ->> server: Remove Subscription (id: uniqueIdentifier) server ->> server: Update cookies in HttpResponse server -->>- client: OK provider ->> provider: Generate AuthenticationState provider -->>- server: AuthenticationState