diff --git a/ProjectTemplates/ShinyApp/GlobalUsings.cs b/ProjectTemplates/ShinyApp/GlobalUsings.cs index 31f26ce..9fa7826 100644 --- a/ProjectTemplates/ShinyApp/GlobalUsings.cs +++ b/ProjectTemplates/ShinyApp/GlobalUsings.cs @@ -21,6 +21,7 @@ global using ReactiveUI.Fody.Helpers; #endif #if shinyframework || prism +global using Prism.AppModel; global using Prism.Navigation; global using Prism.Services; #endif diff --git a/ProjectTemplates/ShinyAspNet/Documentation.md b/ProjectTemplates/ShinyAspNet/Documentation.md index 1b11638..f2ab442 100644 --- a/ProjectTemplates/ShinyAspNet/Documentation.md +++ b/ProjectTemplates/ShinyAspNet/Documentation.md @@ -15,7 +15,9 @@ Created by [Allan Ritchie](https://github.com/aritchie) * [GitHub](https://github.com/shinyorg/mediator) * [Documentation](https://shinylib.net/client/mediator) +## ASP.NET Health Checks +* [Documentation](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-8.0) ## Microsoft Orleans diff --git a/ProjectTemplates/ShinyAspNet/Handlers/Auth/Contracts.cs b/ProjectTemplates/ShinyAspNet/Handlers/Auth/Contracts.cs index 4a28273..58c1bee 100644 --- a/ProjectTemplates/ShinyAspNet/Handlers/Auth/Contracts.cs +++ b/ProjectTemplates/ShinyAspNet/Handlers/Auth/Contracts.cs @@ -3,7 +3,7 @@ namespace ShinyAspNet.Handlers.Auth; public record SignOutRequest( string RefreshToken, string? PushToken -) : IRequest; +) : Shiny.Mediator.IRequest; public record SignInRequest(string Scheme) : IRequest { } diff --git a/ProjectTemplates/ShinyAspNet/Handlers/Auth/RefreshHandler.cs b/ProjectTemplates/ShinyAspNet/Handlers/Auth/RefreshHandler.cs index 3523af6..4759bf2 100644 --- a/ProjectTemplates/ShinyAspNet/Handlers/Auth/RefreshHandler.cs +++ b/ProjectTemplates/ShinyAspNet/Handlers/Auth/RefreshHandler.cs @@ -14,9 +14,9 @@ public async Task Handle(RefreshRequest request, CancellationTo var refreshToken = await data .RefreshTokens .Include(x => x.User) - .FirstOrDefaultAsync(x => x.Id == request.Token); + .FirstOrDefaultAsync(x => x.Id == request.Token, cancellationToken); - var tokens = await jwtService.CreateJwt(refreshToken.User); + var tokens = await jwtService.CreateJwt(refreshToken.User, cancellationToken); return RefreshResponse.Successful(tokens.Jwt, tokens.RefreshToken); } diff --git a/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignInHandler.cs b/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignInHandler.cs index 5497de2..81049bd 100644 --- a/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignInHandler.cs +++ b/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignInHandler.cs @@ -19,7 +19,7 @@ public async Task Handle(SignInRequest request, CancellationToke if (user1 == null) return SignInResponse.Fail; - var uritest = await this.CreateTokenToApp(user1!, false); + var uritest = await this.CreateTokenToApp(user1!, false, cancellationToken); return SignInResponse.Sucessful(uritest); } #endif @@ -33,9 +33,9 @@ public async Task Handle(SignInRequest request, CancellationToke return SignInResponse.Fail; var newUser = false; - var claims = auth.Principal.Identities.FirstOrDefault()?.Claims; + var claims = auth.Principal.Identities.FirstOrDefault()?.Claims.ToList(); var email = claims?.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value; - var user = await data.Users.FirstOrDefaultAsync(x => x.Email == email); + var user = await data.Users.FirstOrDefaultAsync(x => x.Email == email, cancellationToken); if (user == null) { @@ -51,7 +51,7 @@ public async Task Handle(SignInRequest request, CancellationToke user.LastName = claims!.First(x => x.Type == ClaimTypes.Surname).Value; await data.SaveChangesAsync(); - var uri = await this.CreateTokenToApp(user, newUser); + var uri = await this.CreateTokenToApp(user, newUser, cancellationToken); return SignInResponse.Sucessful(uri); } diff --git a/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignOutHandler.cs b/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignOutHandler.cs index 23b25c0..14beefb 100644 --- a/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignOutHandler.cs +++ b/ProjectTemplates/ShinyAspNet/Handlers/Auth/SignOutHandler.cs @@ -14,6 +14,6 @@ await data x.UserId == userId && x.Id == request.RefreshToken ) - .ExecuteDeleteAsync(); + .ExecuteDeleteAsync(cancellationToken); } } \ No newline at end of file diff --git a/ProjectTemplates/ShinyAspNet/Program.cs b/ProjectTemplates/ShinyAspNet/Program.cs index 18ad7fa..6938842 100644 --- a/ProjectTemplates/ShinyAspNet/Program.cs +++ b/ProjectTemplates/ShinyAspNet/Program.cs @@ -43,6 +43,10 @@ }); }); +builder.Services + .AddHealthChecks() + .AddDbContextCheck(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddHttpContextAccessor(); @@ -243,4 +247,8 @@ }); #endif +app + .MapHealthChecks("/health") + .RequireHost("*:5001"); + app.Run(); \ No newline at end of file diff --git a/ProjectTemplates/ShinyAspNet/Services/JwtService.cs b/ProjectTemplates/ShinyAspNet/Services/JwtService.cs index 68bfcb6..53a3db0 100644 --- a/ProjectTemplates/ShinyAspNet/Services/JwtService.cs +++ b/ProjectTemplates/ShinyAspNet/Services/JwtService.cs @@ -28,18 +28,18 @@ public JwtService(IConfiguration cfg, AppDbContext data) } - public async Task<(string Jwt, string RefreshToken)> CreateJwt(User user) + public async Task<(string Jwt, string RefreshToken)> CreateJwt(User user, CancellationToken cancellationToken) { var jwtString = this.CreateJwtString( TimeSpan.FromMinutes(this.tokenExpiryMins), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()) ); - var rtoken = await this.CreateRefreshToken(user); + var rtoken = await this.CreateRefreshToken(user, cancellationToken); return (jwtString, rtoken); } - public async Task ValidateRefreshToken(string token) + public async Task ValidateRefreshToken(string token, CancellationToken cancellationToken) { try { @@ -60,7 +60,10 @@ public async Task ValidateRefreshToken(string token) var storeToken = await this.data .RefreshTokens .Include(x => x.User) - .FirstOrDefaultAsync(x => x.Id == token); + .FirstOrDefaultAsync( + x => x.Id == token, + cancellationToken + ); if (storeToken == null) return false; @@ -97,7 +100,7 @@ string CreateJwtString(TimeSpan untilExpiry, params Claim[] claims) } - async Task CreateRefreshToken(User user) + async Task CreateRefreshToken(User user, CancellationToken cancellationToken) { var jwtString = this.CreateJwtString( TimeSpan.FromHours(this.refreshExpiryHours), @@ -110,11 +113,11 @@ async Task CreateRefreshToken(User user) DateCreated = DateTimeOffset.UtcNow, UserId = user.Id }); - await data.SaveChangesAsync(); + await data.SaveChangesAsync(cancellationToken); return jwtString; } - SymmetricSecurityKey GetSigningKey() => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.signingKey)); - SigningCredentials GetJwtKey() => new SigningCredentials(this.GetSigningKey(), SecurityAlgorithms.HmacSha256); + SymmetricSecurityKey GetSigningKey() => new(Encoding.UTF8.GetBytes(this.signingKey)); + SigningCredentials GetJwtKey() => new(this.GetSigningKey(), SecurityAlgorithms.HmacSha256); } \ No newline at end of file diff --git a/ProjectTemplates/ShinyAspNet/ShinyAspNet.csproj b/ProjectTemplates/ShinyAspNet/ShinyAspNet.csproj index bb6584f..f6f4202 100644 --- a/ProjectTemplates/ShinyAspNet/ShinyAspNet.csproj +++ b/ProjectTemplates/ShinyAspNet/ShinyAspNet.csproj @@ -15,6 +15,7 @@ + @@ -62,6 +63,8 @@ + + diff --git a/Template.csproj b/Template.csproj index ddca78e..a48071d 100644 --- a/Template.csproj +++ b/Template.csproj @@ -3,7 +3,7 @@ Shiny.NET Templates - One stop shop to setup almost everything you can imagine within your .NET MAUI application Template - 2.52.0 + 2.53.0 Shiny.Templates Shiny Templates Allan Ritchie