Add API key authorisation.
This commit is contained in:
parent
f8e8774d8c
commit
c041c42dcf
8 changed files with 124 additions and 5 deletions
|
@ -18,10 +18,16 @@ steps:
|
|||
environment:
|
||||
ConnectionString:
|
||||
from_secret: ConnectionString
|
||||
AuthEndpoint:
|
||||
from_secret: AuthEndpoint
|
||||
AuthApiKey:
|
||||
from_secret: AuthApiKey
|
||||
commands:
|
||||
- chmod +x ./build.sh
|
||||
- ./build.sh
|
||||
- sed -i "s/<DatabaseConnectionString>/$ConnectionString/g" output/appsettings.json
|
||||
- sed -i "s/<AuthEndpoint>/$AuthEndpoint/g" output/appsettings.json
|
||||
- sed -i "s/<AuthApiKey>/$AuthApiKey/g" output/appsettings.json
|
||||
- cp api.blog.service output/
|
||||
- cp -r ./output/* /output
|
||||
- name: restart service
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robware.Api.Blog.Authentication {
|
||||
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions> {
|
||||
private const string ApiKeyHeaderName = "X-Api-Key";
|
||||
private readonly ApiKeyValidator _apiKeyValidator;
|
||||
|
||||
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
ApiKeyValidator apiKeyValidator)
|
||||
: base(options, logger, encoder, clock) {
|
||||
_apiKeyValidator = apiKeyValidator;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
|
||||
if (!Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKeyHeaderValues)) {
|
||||
return AuthenticateResult.NoResult();
|
||||
}
|
||||
|
||||
var apiKey = apiKeyHeaderValues.FirstOrDefault();
|
||||
|
||||
if (apiKeyHeaderValues.Count == 0 || string.IsNullOrWhiteSpace(apiKey)) {
|
||||
return AuthenticateResult.NoResult();
|
||||
}
|
||||
|
||||
if (await _apiKeyValidator.Validate(apiKey)) {
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ApiKeyHeaderName, apiKey)
|
||||
};
|
||||
|
||||
var identity = new ClaimsIdentity(claims, Options.AuthenticationType);
|
||||
var principal = new ClaimsPrincipal(new[] { identity });
|
||||
var ticket = new AuthenticationTicket(principal, Options.Scheme);
|
||||
|
||||
return AuthenticateResult.Success(ticket);
|
||||
}
|
||||
|
||||
return AuthenticateResult.Fail("Invalid API Key provided.");
|
||||
}
|
||||
|
||||
protected override async Task HandleChallengeAsync(AuthenticationProperties properties) {
|
||||
Response.StatusCode = 401;
|
||||
}
|
||||
|
||||
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties) {
|
||||
Response.StatusCode = 403;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace Robware.Api.Blog.Authentication {
|
||||
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions {
|
||||
public const string DefaultScheme = "API Key";
|
||||
public string Scheme => DefaultScheme;
|
||||
public string AuthenticationType = DefaultScheme;
|
||||
}
|
||||
}
|
18
src/Robware.Api.Blog/Authentication/ApiKeyValidator.cs
Normal file
18
src/Robware.Api.Blog/Authentication/ApiKeyValidator.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robware.Api.Blog.Authentication
|
||||
{
|
||||
public class ApiKeyValidator {
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public ApiKeyValidator(HttpClient httpClient) {
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task<bool> Validate(string apiKey) {
|
||||
var response = await _httpClient.GetAsync("api/validate?key=" + apiKey);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
using System;
|
||||
|
||||
namespace Robware.Api.Blog.Authentication {
|
||||
public static class AuthenticationBuilderExtensions {
|
||||
public static AuthenticationBuilder AddApiKeySupport(this AuthenticationBuilder authenticationBuilder, Action<ApiKeyAuthenticationOptions> options) {
|
||||
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationOptions.DefaultScheme, options);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.10.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -4,8 +4,10 @@ using Microsoft.Extensions.Configuration;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MongoDB.Driver;
|
||||
using Robware.Api.Blog.Authentication;
|
||||
using Robware.Blog;
|
||||
using Robware.Data;
|
||||
using System;
|
||||
|
||||
namespace Robware.Api.Blog {
|
||||
public class Startup {
|
||||
|
@ -22,6 +24,17 @@ namespace Robware.Api.Blog {
|
|||
services
|
||||
.AddSingleton<IMongoDatabase>(SetupMongoDatabase())
|
||||
.AddSingleton<IBlogRepository, BlogRepository>();
|
||||
|
||||
services.AddHttpClient<ApiKeyValidator>(client => {
|
||||
client.BaseAddress = new Uri(Configuration["authEndpoint"]);
|
||||
client.DefaultRequestHeaders.Add("x-api-key", new[] {Configuration["authApiKey"]});
|
||||
});
|
||||
|
||||
services.AddAuthentication(options => {
|
||||
options.DefaultAuthenticateScheme = ApiKeyAuthenticationOptions.DefaultScheme;
|
||||
options.DefaultChallengeScheme = ApiKeyAuthenticationOptions.DefaultScheme;
|
||||
})
|
||||
.AddApiKeySupport(options => { });
|
||||
}
|
||||
|
||||
private IMongoDatabase SetupMongoDatabase() {
|
||||
|
@ -39,10 +52,12 @@ namespace Robware.Api.Blog {
|
|||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints => {
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapControllers().RequireAuthorization();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,5 +16,7 @@
|
|||
},
|
||||
"ConnectionStrings": {
|
||||
"database": "<DatabaseConnectionString>"
|
||||
}
|
||||
},
|
||||
"authEndpoint": "<AuthEndpoint>",
|
||||
"authApiKey": "<AuthApiKey>"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue