Files
pas/Api/Program.cs
Marek Lesko f34d523413 feat: Implement OAuth2 authentication with Microsoft, Google, and PocketId
- 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.
2025-11-07 19:23:21 +00:00

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();
}
}
}