- Added JWT configuration to appsettings.json for secure token handling. - Updated config.json to include OAuth provider details for Microsoft, Google, and PocketId. - Added Microsoft icon SVG for UI representation. - Refactored app.config.ts to use a custom AuthInterceptor for managing access tokens. - Enhanced auth route guard to handle asynchronous authentication checks. - Created new auth models for structured request and response handling. - Developed a callback component to manage user login states and transitions. - Updated side-login component to support multiple OAuth providers with loading states. - Implemented authentication service methods for handling OAuth login flows and token management. - Added error handling and user feedback for authentication processes.
180 lines
7.5 KiB
C#
Executable File
180 lines
7.5 KiB
C#
Executable File
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.Identity.Abstractions;
|
|
using Microsoft.Identity.Web;
|
|
using Microsoft.Identity.Web.Resource;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using System.Text;
|
|
using Api.Services;
|
|
|
|
namespace Api
|
|
{
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Api.Models;
|
|
using Microsoft.AspNetCore.Rewrite;
|
|
|
|
public static class Program
|
|
{
|
|
public static void Main(string[] args)
|
|
{
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add services to the container.
|
|
|
|
// Configure JWT authentication for custom tokens
|
|
var jwtSettings = builder.Configuration.GetSection("Jwt");
|
|
var secretKey = jwtSettings["SecretKey"];
|
|
if (string.IsNullOrEmpty(secretKey))
|
|
{
|
|
throw new InvalidOperationException("JWT SecretKey must be configured");
|
|
}
|
|
|
|
builder.Services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
})
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)),
|
|
ValidateIssuer = true,
|
|
ValidIssuer = jwtSettings["Issuer"],
|
|
ValidateAudience = true,
|
|
ValidAudience = jwtSettings["Audience"],
|
|
ValidateLifetime = true,
|
|
ClockSkew = TimeSpan.FromMinutes(5)
|
|
};
|
|
|
|
options.Events = new JwtBearerEvents
|
|
{
|
|
OnAuthenticationFailed = context =>
|
|
{
|
|
var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>()
|
|
.CreateLogger("JwtAuthentication");
|
|
logger.LogWarning("JWT authentication failed: {Exception}", context.Exception.Message);
|
|
return Task.CompletedTask;
|
|
},
|
|
OnTokenValidated = context =>
|
|
{
|
|
var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>()
|
|
.CreateLogger("JwtAuthentication");
|
|
var userId = context.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
|
logger.LogInformation("JWT token validated for user {UserId}", userId);
|
|
return Task.CompletedTask;
|
|
}
|
|
};
|
|
});
|
|
|
|
// Add authorization
|
|
builder.Services.AddAuthorization();
|
|
|
|
// Register custom services
|
|
builder.Services.AddHttpClient<IOAuthValidationService, OAuthValidationService>();
|
|
builder.Services.AddScoped<IJwtService, JwtService>();
|
|
builder.Services.AddScoped<IOAuthValidationService, OAuthValidationService>();
|
|
|
|
builder.Services.AddCors(options =>
|
|
{
|
|
options.AddPolicy("Default", policy =>
|
|
{
|
|
var allowedHostsConfiguration = builder.Configuration["CorsOrigins"]?
|
|
.ToString()
|
|
.Split(',');
|
|
|
|
policy
|
|
.WithOrigins(allowedHostsConfiguration ?? new[] { "*" })
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod()
|
|
.AllowCredentials(); // Allow credentials for JWT tokens
|
|
});
|
|
});
|
|
|
|
builder.Services.AddControllers();
|
|
|
|
// Add DbContext with SQL Server
|
|
builder.Services.AddDbContext<AppDbContext>(options =>
|
|
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
|
|
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen(options =>
|
|
{
|
|
options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
|
{
|
|
Name = "Authorization",
|
|
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
|
|
Scheme = "Bearer",
|
|
BearerFormat = "JWT",
|
|
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
|
|
Description = "JWT Authorization header using the Bearer scheme."
|
|
});
|
|
|
|
options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
|
|
{
|
|
{
|
|
new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
|
{
|
|
Reference = new Microsoft.OpenApi.Models.OpenApiReference
|
|
{
|
|
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
|
|
Id = "Bearer"
|
|
}
|
|
},
|
|
Array.Empty<string>()
|
|
}
|
|
});
|
|
});
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseHttpsRedirection();
|
|
}
|
|
|
|
// Angular rewrite for SPA hosting
|
|
var routes = new[] { "api", "swagger" };
|
|
var rewriteString = String.Join("|", routes);
|
|
var rewriteOptions = new RewriteOptions()
|
|
.AddRewrite(@$"^(?!.*?\b({rewriteString}))^(?!.*?\.\b(jpg|jpeg|png|svg|ttf|woff|woff2|html|js|json|css|ico))", "index.html", false);
|
|
app.UseRewriter(rewriteOptions);
|
|
|
|
// Serve static files from the Angular app
|
|
if (app.Environment.IsDevelopment() && Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "../Web/dist/Web/browser")))
|
|
{
|
|
var currentDirectory = Directory.GetCurrentDirectory();
|
|
var staticFilePath = Path.Combine(currentDirectory, "../Web/dist/Web/browser");
|
|
app.UseDefaultFiles(new DefaultFilesOptions
|
|
{
|
|
FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(staticFilePath),
|
|
DefaultFileNames = new List<string> { "index.html" }
|
|
});
|
|
app.UseStaticFiles(new StaticFileOptions
|
|
{
|
|
FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(staticFilePath),
|
|
RequestPath = ""
|
|
});
|
|
}
|
|
else
|
|
{
|
|
app.UseDefaultFiles(); // Uses wwwroot by default
|
|
app.UseStaticFiles();
|
|
}
|
|
|
|
app.UseCors("Default");
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapControllers();
|
|
|
|
app.Run();
|
|
}
|
|
}
|
|
}
|