1
0

Add JWT authentication and authorization to API and SignalR

This commit is contained in:
Márcio Eric 2026-01-14 19:54:17 -03:00
parent e8f06e46f0
commit 3e182baf7e
4 changed files with 71 additions and 14 deletions

View File

@ -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<ScraperOptions>(builder.Configuration.GetSection("Scraper"));
builder.Services.AddSingleton<IDomainRateLimiter>(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<ScrapeHub>("/ws/scrape");
app.MapHub<ScrapeHub>("/ws/scrape").RequireAuthorization();
// app.UseHttpsRedirection();

View File

@ -1,23 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="Npgsql" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.1" />
</ItemGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>
</Project>

View File

@ -4,5 +4,10 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Authentication": {
"Authority": "https://auth.evolucao.io/application/o/web-scrapper/",
"Audience": "qbwOof0fnJzIQhiDsM0Kd41dw7YB0Ab15FbnZxHM",
"RequireHttpsMetadata": true
}
}

View File

@ -20,5 +20,10 @@
"MaxDelayMs": 8000
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"Authentication": {
"Authority": "https://auth.evolucao.io/application/o/web-scrapper/",
"Audience": "qbwOof0fnJzIQhiDsM0Kd41dw7YB0Ab15FbnZxHM",
"RequireHttpsMetadata": true
}
}