Authentication
Live on this serverSecures an API with API keys, roles and data per user.
using GenHTTP.Api.Content;
using GenHTTP.Api.Content.Authentication;
using GenHTTP.Api.Protocol;
using GenHTTP.Modules.Authentication;
using GenHTTP.Modules.Layouting;
using GenHTTP.Modules.Reflection;
using GenHTTP.Modules.Reflection.Injectors;
using GenHTTP.Modules.Webservices;
public static class AuthenticationSample
{
public static IHandlerBuilder Create()
{
/*
*
* A notes API secured with API keys. Every user only sees their own notes,
* while administrators may see the notes of all users.
*
* Clients send their key via the "X-API-Key" header: "alice-key" for a user,
* "bob-key" for an administrator.
*
* Basic, bearer (JWT) and client certificate authentication work the same way.
*
* See https://genhttp.org/documentation/content/concerns/authentication/
*
*/
var auth = ApiKeyAuthentication.Create()
.Authenticator(AuthenticateAsync);
// allows the service methods to declare the user as a parameter
var injection = Injection.Default()
.Add(new UserInjector<NoteUser>());
return Layout.Create()
.AddService<NoteService>("notes", injectors: injection)
.Authentication(auth);
}
private static ValueTask<IUser?> AuthenticateAsync(IRequest request, string key)
{
// this is where you would look up the key in your database
IUser? user = key switch
{
"alice-key" => new NoteUser("alice", "USER"),
"bob-key" => new NoteUser("bob", "USER", "ADMIN"),
_ => null // unknown key, access denied
};
return new(user);
}
public record NoteUser(string Name, params string[] Roles) : IUser
{
public string DisplayName => Name;
}
public record Note(string Owner, string Text);
public class NoteService
{
private static readonly List<Note> Notes = [];
// GET /samples/authentication/notes/
[ResourceMethod]
public List<Note> Mine(NoteUser user)
{
lock (Notes) return Notes.Where(n => n.Owner == user.Name).ToList();
}
// PUT /samples/authentication/notes/ with the text as body
[ResourceMethod(Method.Put)]
public Note Add(NoteUser user, [FromBody] string text)
{
var note = new Note(user.Name, text);
lock (Notes)
{
// keeps the latest notes only
if (Notes.Count >= 50) Notes.RemoveAt(0);
Notes.Add(note);
}
return note;
}
// GET /samples/authentication/notes/all
[ResourceMethod("all")]
[RequireRole("ADMIN")]
public List<Note> All()
{
lock (Notes) return [..Notes];
}
}
}Try it
Send the requests to the sample running on this server - change the path, headers or body as you like, or copy them as curl commands.
Add a note as Alice
curl -i -X PUT -H 'X-API-Key: alice-key' -H 'Content-Type: text/plain' -d 'Buy milk' 'https://genhttp.org/samples/authentication/notes/'List the notes of Alice
curl -i -H 'X-API-Key: alice-key' 'https://genhttp.org/samples/authentication/notes/'List all notes as Alice
403, Alice is no administrator
curl -i -H 'X-API-Key: alice-key' 'https://genhttp.org/samples/authentication/notes/all'List all notes as Bob
curl -i -H 'X-API-Key: bob-key' 'https://genhttp.org/samples/authentication/notes/all'Call without a key
401
curl -i 'https://genhttp.org/samples/authentication/notes/'