Add APIKey authentication

This commit is contained in:
2023-11-22 20:53:06 -05:00
parent 38c3895459
commit 15f8385005
10 changed files with 179 additions and 26 deletions

14
.gitignore vendored
View File

@@ -399,12 +399,12 @@ FodyWeavers.xsd
*.sln.iml
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# .vscode/*
# !.vscode/settings.json
# !.vscode/tasks.json
# !.vscode/launch.json
# !.vscode/extensions.json
# !.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
@@ -450,7 +450,7 @@ project.lock.json
nupkg/
# Visual Studio Code
.vscode
# .vscode
# Rider
.idea

37
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"launchSettingsProfile": "http",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net8.0/TodoApi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

41
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/TodoApi.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/TodoApi.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/TodoApi.sln"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,24 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Helpers;
using TodoApi.Models;
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
[ApiKey]
public class TodoItemsController(TodoContext context) : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
private readonly TodoContext _context = context;
// GET: api/TodoItems
[HttpGet]

View File

@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace TodoApi.Helpers;
public class ApiKeyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// Get the required service to validate the API key
var apiKeyValidator = context.HttpContext.RequestServices.GetRequiredService<IApiKeyValidator>();
// Get the API key from the X-API-KEY header
var apiKey = context.HttpContext.Request.Headers["X-API-KEY"];
// Validate the API key using the IApiKeyValidator service
if (string.IsNullOrEmpty(apiKey) || !apiKeyValidator.Validate(apiKey))
{
// If the API key is invalid, set the response status code to 401 Unauthorized
context.Result = new UnauthorizedResult();
return;
}
// If the API key is valid, continue with the action execution
base.OnActionExecuting(context);
}
}

View File

@@ -0,0 +1,20 @@
namespace TodoApi.Helpers
{
public interface IApiKeyValidator
{
bool Validate(string? apiKey);
}
public class ApiKeyValidator(List<string>? apiKeys) : IApiKeyValidator
{
private readonly List<string>? _apiKeys = apiKeys;
public bool Validate(string? apiKey)
{
if (_apiKeys == null) return false;
// Verify the provided apiKey is in our configuration
return _apiKeys.Contains(apiKey!.ToLower());
}
}
}

View File

@@ -1,33 +1,64 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Data.Sqlite;
using TodoApi.Helpers;
using TodoApi.Models;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
//Read the SQLite connection string from config file and add a DBContext
var connectionString = new SqliteConnectionStringBuilder(builder.Configuration.GetConnectionString("TodoDatabase"))
{
Mode = SqliteOpenMode.ReadWriteCreate
}.ToString();
builder.Services.AddDbContext<TodoContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDbContext<TodoContext>(options => options.UseSqlite(connectionString));
//setup APIKey validation using APIKeys from config file
var apiKeys = builder.Configuration.GetSection("Authentication").GetValue<List<string>>("APIKeys");
builder.Services.AddSingleton<IApiKeyValidator, ApiKeyValidator>(_ => new ApiKeyValidator(apiKeys));
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ServiceName", Version = "1" });
c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Name = "X-API-KEY",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Description = "Authorization by X-API-KEY inside request's header",
Scheme = "ApiKeyScheme"
});
var key = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey"
},
In = ParameterLocation.Header
};
var requirement = new OpenApiSecurityRequirement { { key, new List<string>() } };
c.AddSecurityRequirement(requirement);
});
var app = builder.Build();
// Configure the HTTP request pipeline.
// if (app.Environment.IsDevelopment())
// {
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwagger();
app.UseSwaggerUI();
// }
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

View File

@@ -2,6 +2,11 @@
"ConnectionStrings": {
"TodoDatabase": "Data Source=data/todo.db"
},
"Authentication": {
"APIKeys": [
"0a7eefbb-17f5-4299-882b-94719461a896"
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
@@ -9,4 +14,4 @@
}
},
"AllowedHosts": "*"
}
}

Binary file not shown.

View File