Added new web api project for testing; AcceptTokenFromQuery feature in NDB.Security.Authentication.Identity
parent
32d8dc0c4e
commit
25ea2cc4f1
|
@ -8,5 +8,6 @@ namespace NDB.Security.Authentication.Identity.Abstractions
|
||||||
Func<HttpRequest, bool> AuthenticateAsGuest { get; }
|
Func<HttpRequest, bool> AuthenticateAsGuest { get; }
|
||||||
int GuestUserId { get; }
|
int GuestUserId { get; }
|
||||||
string GuestUserName { get; }
|
string GuestUserName { get; }
|
||||||
|
bool AcceptTokenFromQuery { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace NDB.Security.Authentication.Identity
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, string identityServerBaseAddress)
|
public static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, string identityServerBaseAddress)
|
||||||
{
|
{
|
||||||
services.AddIdentityAuthentication(identityServerBaseAddress, new Services.AuthenticationOptions());
|
services.AddIdentityAuthentication(identityServerBaseAddress, new Models.AuthenticationOptions());
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NDB.Security.Authentication.Identity.Constants
|
||||||
|
{
|
||||||
|
internal struct QueryParams
|
||||||
|
{
|
||||||
|
public const string
|
||||||
|
Token = "token";
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NDB.Security.Authentication.Identity.Abstractions;
|
using NDB.Security.Authentication.Identity.Abstractions;
|
||||||
|
using c = NDB.Security.Authentication.Identity.Constants;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
@ -27,35 +28,54 @@ namespace NDB.Security.Authentication.Identity
|
||||||
|
|
||||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||||
{
|
{
|
||||||
if (!Request.Headers.ContainsKey("Authorization"))
|
var token = GetAuthorizationToken();
|
||||||
|
if (token != null)
|
||||||
{
|
{
|
||||||
var authenticateAsGuest = _authenticationOptions.AuthenticateAsGuest?.Invoke(Request) ?? false;
|
TokenCore tokenCore;
|
||||||
if (authenticateAsGuest)
|
try
|
||||||
{
|
{
|
||||||
var guestTicket = GetGuestAuthenticationTicket(_authenticationOptions.GuestUserId, _authenticationOptions.GuestUserName);
|
tokenCore = await _identityService.Authorize(token);
|
||||||
return AuthenticateResult.Success(guestTicket);
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Fail("Invalid authorization");
|
||||||
}
|
}
|
||||||
|
|
||||||
return AuthenticateResult.Fail("Missing Authorization Header");
|
if (tokenCore == null)
|
||||||
|
return AuthenticateResult.Fail("Invalid token");
|
||||||
|
|
||||||
|
var ticket = GetAuthenticationTicket(tokenCore);
|
||||||
|
return AuthenticateResult.Success(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenCore tokenCore;
|
var authenticateAsGuest = _authenticationOptions.AuthenticateAsGuest?.Invoke(Request) ?? false;
|
||||||
try
|
if (authenticateAsGuest)
|
||||||
|
{
|
||||||
|
var guestTicket = GetGuestAuthenticationTicket(_authenticationOptions.GuestUserId, _authenticationOptions.GuestUserName);
|
||||||
|
return AuthenticateResult.Success(guestTicket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuthenticateResult.Fail("Missing authorization header");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAuthorizationToken()
|
||||||
|
{
|
||||||
|
if (Request.Headers.ContainsKey("Authorization"))
|
||||||
{
|
{
|
||||||
var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
|
var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
|
||||||
var token = authorizationHeader.Parameter;
|
var token = authorizationHeader.Parameter;
|
||||||
tokenCore = await _identityService.Authorize(token);
|
return token;
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
|
if (_authenticationOptions.AcceptTokenFromQuery
|
||||||
|
&& Request.Query.Count > 0
|
||||||
|
&& Request.Query.ContainsKey(c.QueryParams.Token))
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Fail("Invalid authorization header");
|
var token = Request.Query[c.QueryParams.Token];
|
||||||
|
return token.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenCore == null)
|
return null;
|
||||||
return AuthenticateResult.Fail("Invalid token");
|
|
||||||
|
|
||||||
var ticket = GetAuthenticationTicket(tokenCore);
|
|
||||||
return AuthenticateResult.Success(ticket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthenticationTicket GetGuestAuthenticationTicket(int guestId, string guestName)
|
private AuthenticationTicket GetGuestAuthenticationTicket(int guestId, string guestName)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
using NDB.Security.Authentication.Identity.Abstractions;
|
using NDB.Security.Authentication.Identity.Abstractions;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace NDB.Security.Authentication.Identity.Services
|
namespace NDB.Security.Authentication.Identity.Models
|
||||||
{
|
{
|
||||||
public class AuthenticationOptions : IAuthenticationOptions
|
public class AuthenticationOptions : IAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
@ -11,5 +11,6 @@ namespace NDB.Security.Authentication.Identity.Services
|
||||||
public int GuestUserId { get; set; }
|
public int GuestUserId { get; set; }
|
||||||
|
|
||||||
public string GuestUserName { get; set; }
|
public string GuestUserName { get; set; }
|
||||||
|
public bool AcceptTokenFromQuery { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NDB.Test.Api.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class DebugController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<DebugController> _logger;
|
||||||
|
|
||||||
|
public DebugController(ILogger<DebugController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet("ping")]
|
||||||
|
public IActionResult Ping()
|
||||||
|
{
|
||||||
|
return Ok($"Ping success. System datetime: {DateTime.Now}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("ping-auth")]
|
||||||
|
public IActionResult PingAuth()
|
||||||
|
{
|
||||||
|
return Ok($"Secured ping success. System datetime: {DateTime.Now}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NDB.Security.Authentication.Identity;
|
||||||
|
using NDB.Security.Authentication.Identity.Models;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NDB.Test.Api.Extensions
|
||||||
|
{
|
||||||
|
public static class AuthenticationExtensions
|
||||||
|
{
|
||||||
|
private record GuestRoute(string Route, int PathElements);
|
||||||
|
private static readonly GuestRoute[] _resourcesRoutes = new GuestRoute[]
|
||||||
|
{
|
||||||
|
new GuestRoute("/resources", 1),
|
||||||
|
new GuestRoute("/images", 1),
|
||||||
|
new GuestRoute("/spot", 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, string identityServerBaseAddress)
|
||||||
|
{
|
||||||
|
var authenticationOptions = new AuthenticationOptions()
|
||||||
|
{
|
||||||
|
AuthenticateAsGuest = (HttpRequest request) =>
|
||||||
|
{
|
||||||
|
var authenticateAsGuest = AuthenticateAsGuest(request);
|
||||||
|
return authenticateAsGuest;
|
||||||
|
},
|
||||||
|
GuestUserName = "Guest",
|
||||||
|
GuestUserId = -111,
|
||||||
|
AcceptTokenFromQuery = true
|
||||||
|
};
|
||||||
|
|
||||||
|
services.AddIdentityAuthentication(identityServerBaseAddress, authenticationOptions);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AuthenticateAsGuest(HttpRequest request)
|
||||||
|
{
|
||||||
|
if (!request.Path.HasValue)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var guestRoute = _resourcesRoutes.FirstOrDefault(z => request.Path.Value.StartsWith(z.Route));
|
||||||
|
if (guestRoute == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var resourceRequestedById = request.Query.Count > 0 && request.Query.ContainsKey("id");
|
||||||
|
if (resourceRequestedById)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var resourceRequestedByCode = request.Query.Count > 0 && request.Query.ContainsKey("code");
|
||||||
|
if (resourceRequestedByCode)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var resourceRequestedByName = request.Path.Value.Replace(guestRoute.Route, string.Empty).Substring(1).Split("/").Length == guestRoute.PathElements;
|
||||||
|
if (resourceRequestedByName)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\NDB.Extensions.Swagger\NDB.Extensions.Swagger.csproj" />
|
||||||
|
<ProjectReference Include="..\NDB.Security.Authentication.Identity\NDB.Security.Authentication.Identity.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NDB.Test.Api
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"NDB.Test.Api": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": "true",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:5000",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using NDB.Extensions.Swagger;
|
||||||
|
using NDB.Extensions.Swagger.Constants;
|
||||||
|
using NDB.Test.Api.Extensions;
|
||||||
|
|
||||||
|
namespace NDB.Test.Api
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
// Add basic authentication
|
||||||
|
services.AddIdentityAuthentication(Configuration.GetSection("IdentityServer")["BaseAddress"]);
|
||||||
|
|
||||||
|
services.AddControllers();
|
||||||
|
services.AddSwagger("NDB.Test.Api", AuthorizationType.InhouseIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NDB.Test.Api v1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
app.ConfigureSwagger("NDB.Test.Api v1");
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"IdentityServer": {
|
||||||
|
//"BaseAddress": "http://localhost:5063/"
|
||||||
|
"BaseAddress": "https://toodle.ddns.net/identity-server-api/"
|
||||||
|
}
|
||||||
|
}
|
16
NDB.sln
16
NDB.sln
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29324.140
|
VisualStudioVersion = 17.0.31903.59
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Hosting.WindowsService", "NDB.Hosting.WindowsService\NDB.Hosting.WindowsService.csproj", "{F4AB0B71-6B14-402B-8B05-128AFB3E06D9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Hosting.WindowsService", "NDB.Hosting.WindowsService\NDB.Hosting.WindowsService.csproj", "{F4AB0B71-6B14-402B-8B05-128AFB3E06D9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -54,7 +54,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Security.Authentication
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "caching", "caching", "{A206A484-3ACF-4032-8B36-AC93A72B0B88}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "caching", "caching", "{A206A484-3ACF-4032-8B36-AC93A72B0B88}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDB.Extensions.Caching", "NDB.Extensions.Caching\NDB.Extensions.Caching.csproj", "{3E045EE6-A290-467C-B503-3A6CB0065C97}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Extensions.Caching", "NDB.Extensions.Caching\NDB.Extensions.Caching.csproj", "{3E045EE6-A290-467C-B503-3A6CB0065C97}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CCEE458E-02A8-42FD-8F5F-A35481A23303}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDB.Test.Api", "NDB.Test.Api\NDB.Test.Api.csproj", "{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -102,6 +106,10 @@ Global
|
||||||
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -127,6 +135,8 @@ Global
|
||||||
{5C0637C8-6BA4-4EAE-97CA-BB8D98B2991A} = {B8132F39-6677-4D70-84CA-9747DC9086B3}
|
{5C0637C8-6BA4-4EAE-97CA-BB8D98B2991A} = {B8132F39-6677-4D70-84CA-9747DC9086B3}
|
||||||
{A206A484-3ACF-4032-8B36-AC93A72B0B88} = {B50B55F0-9E6E-4061-9100-E2329A44E76B}
|
{A206A484-3ACF-4032-8B36-AC93A72B0B88} = {B50B55F0-9E6E-4061-9100-E2329A44E76B}
|
||||||
{3E045EE6-A290-467C-B503-3A6CB0065C97} = {A206A484-3ACF-4032-8B36-AC93A72B0B88}
|
{3E045EE6-A290-467C-B503-3A6CB0065C97} = {A206A484-3ACF-4032-8B36-AC93A72B0B88}
|
||||||
|
{CCEE458E-02A8-42FD-8F5F-A35481A23303} = {E0202271-4E92-4DB8-900D-B5FD745B9278}
|
||||||
|
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01} = {CCEE458E-02A8-42FD-8F5F-A35481A23303}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {87541BAB-3FAC-4ADB-A7FB-8228DA87843D}
|
SolutionGuid = {87541BAB-3FAC-4ADB-A7FB-8228DA87843D}
|
||||||
|
|
Loading…
Reference in New Issue