Add APIKey authentication
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -399,12 +399,12 @@ FodyWeavers.xsd
|
|||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
|
||||||
# ---> VisualStudioCode
|
# ---> VisualStudioCode
|
||||||
.vscode/*
|
# .vscode/*
|
||||||
!.vscode/settings.json
|
# !.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
# !.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
# !.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
# !.vscode/extensions.json
|
||||||
!.vscode/*.code-snippets
|
# !.vscode/*.code-snippets
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
.history/
|
.history/
|
||||||
@@ -450,7 +450,7 @@ project.lock.json
|
|||||||
nupkg/
|
nupkg/
|
||||||
|
|
||||||
# Visual Studio Code
|
# Visual Studio Code
|
||||||
.vscode
|
# .vscode
|
||||||
|
|
||||||
# Rider
|
# Rider
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
37
.vscode/launch.json
vendored
Normal file
37
.vscode/launch.json
vendored
Normal 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
41
.vscode/tasks.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TodoApi.Helpers;
|
||||||
using TodoApi.Models;
|
using TodoApi.Models;
|
||||||
|
|
||||||
namespace TodoApi.Controllers
|
namespace TodoApi.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class TodoItemsController : ControllerBase
|
[ApiKey]
|
||||||
|
public class TodoItemsController(TodoContext context) : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly TodoContext _context;
|
private readonly TodoContext _context = context;
|
||||||
|
|
||||||
public TodoItemsController(TodoContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET: api/TodoItems
|
// GET: api/TodoItems
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|||||||
27
Helpers/ApiKeyAttribute.cs
Normal file
27
Helpers/ApiKeyAttribute.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Helpers/ApiKeyValidator.cs
Normal file
20
Helpers/ApiKeyValidator.cs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Program.cs
39
Program.cs
@@ -1,22 +1,53 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
|
using TodoApi.Helpers;
|
||||||
using TodoApi.Models;
|
using TodoApi.Models;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
//Read the SQLite connection string from config file and add a DBContext
|
||||||
var connectionString = new SqliteConnectionStringBuilder(builder.Configuration.GetConnectionString("TodoDatabase"))
|
var connectionString = new SqliteConnectionStringBuilder(builder.Configuration.GetConnectionString("TodoDatabase"))
|
||||||
{
|
{
|
||||||
Mode = SqliteOpenMode.ReadWriteCreate
|
Mode = SqliteOpenMode.ReadWriteCreate
|
||||||
}.ToString();
|
}.ToString();
|
||||||
builder.Services.AddDbContext<TodoContext>(options =>
|
builder.Services.AddDbContext<TodoContext>(options => options.UseSqlite(connectionString));
|
||||||
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
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
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();
|
var app = builder.Build();
|
||||||
|
|
||||||
@@ -27,7 +58,7 @@ var app = builder.Build();
|
|||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseAuthentication();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"TodoDatabase": "Data Source=data/todo.db"
|
"TodoDatabase": "Data Source=data/todo.db"
|
||||||
},
|
},
|
||||||
|
"Authentication": {
|
||||||
|
"APIKeys": [
|
||||||
|
"0a7eefbb-17f5-4299-882b-94719461a896"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|||||||
BIN
data/todo.db-shm
BIN
data/todo.db-shm
Binary file not shown.
Reference in New Issue
Block a user