Basic password auth
This commit is contained in:
parent
7b1e4030b0
commit
e6217d543d
5 changed files with 40 additions and 8 deletions
|
@ -135,6 +135,10 @@ pub const ApiServer = struct {
|
|||
return self.db.getBy(models.User, .id, id, alloc);
|
||||
}
|
||||
|
||||
pub fn getUserByHandle(self: *ApiServer, handle: []const u8, alloc: std.mem.Allocator) !?models.User {
|
||||
return self.db.getBy(models.User, .handle, handle, alloc);
|
||||
}
|
||||
|
||||
pub fn react(self: *ApiServer, note_id: Uuid, ctx: ApiContext) !void {
|
||||
try self.db.insert(models.Reaction, .{ .note_id = note_id, .reactor_id = ctx.user_context.user.id });
|
||||
}
|
||||
|
|
|
@ -77,10 +77,15 @@ pub fn createNote(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs)
|
|||
}
|
||||
|
||||
pub fn createUser(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
|
||||
const info = try utils.parseRequestBody(api.CreateInfo(models.User), ctx);
|
||||
defer utils.freeRequestBody(info, ctx.alloc);
|
||||
const credentials = try utils.parseRequestBody(struct { handle: []const u8, password: []const u8 }, ctx);
|
||||
defer utils.freeRequestBody(credentials, ctx.alloc);
|
||||
|
||||
const user = srv.api.createUser(info) catch |err| switch (err) {
|
||||
var hash_buf: [128]u8 = undefined;
|
||||
|
||||
const Hash = std.crypto.pwhash.scrypt;
|
||||
const hashed_password = try Hash.strHash(credentials.password, .{ .allocator = ctx.alloc, .params = Hash.Params.interactive, .encoding = .phc }, &hash_buf);
|
||||
|
||||
const user = srv.api.createUser(.{ .handle = credentials.handle, .hashed_password = hashed_password }) catch |err| switch (err) {
|
||||
error.HandleNotAvailable => return try utils.respondError(ctx, .bad_request, "handle not available"),
|
||||
else => return err,
|
||||
};
|
||||
|
@ -135,6 +140,22 @@ pub fn authenticate(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs
|
|||
try utils.respondJson(ctx, .ok, user_ctx.user_context);
|
||||
}
|
||||
|
||||
pub fn login(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
|
||||
const credentials = try utils.parseRequestBody(struct { username: []const u8, password: []const u8 }, ctx);
|
||||
defer utils.freeRequestBody(credentials, ctx.alloc);
|
||||
|
||||
// TODO: This gives away the existence of a user through a timing side channel. is that acceptable?
|
||||
const user = (try srv.api.getUserByHandle(credentials.username, ctx.alloc)) orelse return utils.respondError(ctx, .bad_request, "Invalid Login");
|
||||
|
||||
const Hash = std.crypto.pwhash.scrypt;
|
||||
Hash.strVerify(user.hashed_password, credentials.password, .{ .allocator = ctx.alloc }) catch |err| switch (err) {
|
||||
error.PasswordVerificationFailed => return utils.respondError(ctx, .bad_request, "Invalid Login"),
|
||||
else => return err,
|
||||
};
|
||||
|
||||
try utils.respondJson(ctx, .ok, .{ .id = user.id });
|
||||
}
|
||||
|
||||
pub fn healthcheck(_: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
|
||||
try utils.respondJson(ctx, .ok, .{ .status = "ok" });
|
||||
}
|
||||
|
|
|
@ -104,7 +104,9 @@ pub const Database = struct {
|
|||
\\CREATE TABLE IF NOT EXISTS
|
||||
\\user(
|
||||
\\ id TEXT PRIMARY KEY,
|
||||
\\ handle TEXT NOT NULL
|
||||
\\ handle TEXT NOT NULL,
|
||||
\\
|
||||
\\ hashed_password TEXT NOT NULL
|
||||
\\) STRICT;
|
||||
,
|
||||
\\CREATE TABLE IF NOT EXISTS
|
||||
|
|
|
@ -17,13 +17,15 @@ const router = Router{
|
|||
Route.new(.GET, "/healthcheck", c.healthcheck),
|
||||
|
||||
Route.new(.GET, "/auth/authenticate", c.authenticate),
|
||||
Route.new(.POST, "/auth/login", c.login),
|
||||
|
||||
Route.new(.GET, "/notes/:id", c.getNote),
|
||||
Route.new(.POST, "/notes", c.createNote),
|
||||
Route.new(.GET, "/notes/:id", c.getNote),
|
||||
Route.new(.GET, "/notes/:id/reacts", c.listReacts),
|
||||
Route.new(.POST, "/notes/:id/reacts", c.react),
|
||||
Route.new(.GET, "/users/:id", c.getUser),
|
||||
|
||||
Route.new(.POST, "/users", c.createUser),
|
||||
Route.new(.GET, "/users/:id", c.getUser),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -43,8 +45,9 @@ pub const RequestServer = struct {
|
|||
defer srv.shutdown();
|
||||
|
||||
while (true) {
|
||||
var buf: [1 << 20]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
||||
const buf = try self.alloc.alloc(u8, 1 << 28); // 4mb
|
||||
defer self.alloc.free(buf);
|
||||
var fba = std.heap.FixedBufferAllocator.init(buf);
|
||||
const alloc = fba.allocator();
|
||||
|
||||
var ctx = try srv.accept(alloc);
|
||||
|
|
|
@ -15,6 +15,8 @@ pub const Note = struct {
|
|||
pub const User = struct {
|
||||
id: Uuid,
|
||||
handle: []const u8,
|
||||
|
||||
hashed_password: []const u8, // encoded in PHC format, with salt
|
||||
};
|
||||
|
||||
pub const Reaction = struct {
|
||||
|
|
Loading…
Reference in a new issue