Add APIKey authentication
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -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
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.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]
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Program.cs
43
Program.cs
@@ -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();
|
||||
|
||||
|
||||
@@ -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": "*"
|
||||
}
|
||||
}
|
||||
BIN
data/todo.db-shm
BIN
data/todo.db-shm
Binary file not shown.
Reference in New Issue
Block a user