diff --git a/ScrapperAPI/Program.cs b/ScrapperAPI/Program.cs index e057e48..181ef69 100644 --- a/ScrapperAPI/Program.cs +++ b/ScrapperAPI/Program.cs @@ -1,3 +1,6 @@ +using Microsoft.IdentityModel.Tokens; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authentication.JwtBearer; using ScrapperAPI.Bus; using ScrapperAPI.Factories; using ScrapperAPI.Hub; @@ -14,6 +17,50 @@ builder.Services.AddOpenApi(); builder.Services.AddSignalR(); builder.Services.AddControllers(); +// Authentik (OIDC) - JWT Bearer validation for API + SignalR +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + // Example: https://auth.seu-dominio.com/application/o/seu-app/ + options.Authority = builder.Configuration["Authentication:Authority"]; + options.RequireHttpsMetadata = builder.Configuration.GetValue("Authentication:RequireHttpsMetadata", true); + + // Usually the SPA client_id + options.Audience = builder.Configuration["Authentication:Audience"]; + + // SignalR sends the token via the query string (access_token) for WebSockets + options.Events = new JwtBearerEvents + { + OnMessageReceived = context => + { + var accessToken = context.Request.Query["access_token"]; + var path = context.HttpContext.Request.Path; + + if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/ws/scrape")) + { + context.Token = accessToken; + } + + return System.Threading.Tasks.Task.CompletedTask; + } + }; + + // If you want stricter token validation, uncomment: + // options.TokenValidationParameters = new TokenValidationParameters + // { + // ValidateIssuer = true, + // ValidateAudience = true, + // }; + }); + +builder.Services.AddAuthorization(options => +{ + // Require authentication by default for ALL endpoints (controllers + hub) + options.FallbackPolicy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); +}); + builder.Services.Configure(builder.Configuration.GetSection("Scraper")); builder.Services.AddSingleton(sp => @@ -50,13 +97,16 @@ var app = builder.Build(); app.UseCors("AllowReact"); +app.UseAuthentication(); +app.UseAuthorization(); + if (app.Environment.IsDevelopment()) { - app.MapOpenApi(); + app.MapOpenApi().AllowAnonymous(); } app.MapControllers(); -app.MapHub("/ws/scrape"); +app.MapHub("/ws/scrape").RequireAuthorization(); // app.UseHttpsRedirection(); diff --git a/ScrapperAPI/ScrapperAPI.csproj b/ScrapperAPI/ScrapperAPI.csproj index 7ea7e5f..355a035 100644 --- a/ScrapperAPI/ScrapperAPI.csproj +++ b/ScrapperAPI/ScrapperAPI.csproj @@ -1,23 +1,20 @@ - net10.0 enable enable Linux - - + + - - - .dockerignore - + + .dockerignore + - - + \ No newline at end of file diff --git a/ScrapperAPI/appsettings.Development.json b/ScrapperAPI/appsettings.Development.json index 0c208ae..033e1bb 100644 --- a/ScrapperAPI/appsettings.Development.json +++ b/ScrapperAPI/appsettings.Development.json @@ -4,5 +4,10 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "Authentication": { + "Authority": "https://auth.evolucao.io/application/o/web-scrapper/", + "Audience": "qbwOof0fnJzIQhiDsM0Kd41dw7YB0Ab15FbnZxHM", + "RequireHttpsMetadata": true } -} +} \ No newline at end of file diff --git a/ScrapperAPI/appsettings.json b/ScrapperAPI/appsettings.json index 51adbe8..613ac2e 100644 --- a/ScrapperAPI/appsettings.json +++ b/ScrapperAPI/appsettings.json @@ -20,5 +20,10 @@ "MaxDelayMs": 8000 } }, - "AllowedHosts": "*" -} + "AllowedHosts": "*", + "Authentication": { + "Authority": "https://auth.evolucao.io/application/o/web-scrapper/", + "Audience": "qbwOof0fnJzIQhiDsM0Kd41dw7YB0Ab15FbnZxHM", + "RequireHttpsMetadata": true + } +} \ No newline at end of file