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);
|
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 {
|
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 });
|
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 {
|
pub fn createUser(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
|
||||||
const info = try utils.parseRequestBody(api.CreateInfo(models.User), ctx);
|
const credentials = try utils.parseRequestBody(struct { handle: []const u8, password: []const u8 }, ctx);
|
||||||
defer utils.freeRequestBody(info, ctx.alloc);
|
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"),
|
error.HandleNotAvailable => return try utils.respondError(ctx, .bad_request, "handle not available"),
|
||||||
else => return err,
|
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);
|
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 {
|
pub fn healthcheck(_: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
|
||||||
try utils.respondJson(ctx, .ok, .{ .status = "ok" });
|
try utils.respondJson(ctx, .ok, .{ .status = "ok" });
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,9 @@ pub const Database = struct {
|
||||||
\\CREATE TABLE IF NOT EXISTS
|
\\CREATE TABLE IF NOT EXISTS
|
||||||
\\user(
|
\\user(
|
||||||
\\ id TEXT PRIMARY KEY,
|
\\ id TEXT PRIMARY KEY,
|
||||||
\\ handle TEXT NOT NULL
|
\\ handle TEXT NOT NULL,
|
||||||
|
\\
|
||||||
|
\\ hashed_password TEXT NOT NULL
|
||||||
\\) STRICT;
|
\\) STRICT;
|
||||||
,
|
,
|
||||||
\\CREATE TABLE IF NOT EXISTS
|
\\CREATE TABLE IF NOT EXISTS
|
||||||
|
|
|
@ -17,13 +17,15 @@ const router = Router{
|
||||||
Route.new(.GET, "/healthcheck", c.healthcheck),
|
Route.new(.GET, "/healthcheck", c.healthcheck),
|
||||||
|
|
||||||
Route.new(.GET, "/auth/authenticate", c.authenticate),
|
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(.POST, "/notes", c.createNote),
|
||||||
|
Route.new(.GET, "/notes/:id", c.getNote),
|
||||||
Route.new(.GET, "/notes/:id/reacts", c.listReacts),
|
Route.new(.GET, "/notes/:id/reacts", c.listReacts),
|
||||||
Route.new(.POST, "/notes/:id/reacts", c.react),
|
Route.new(.POST, "/notes/:id/reacts", c.react),
|
||||||
Route.new(.GET, "/users/:id", c.getUser),
|
|
||||||
Route.new(.POST, "/users", c.createUser),
|
Route.new(.POST, "/users", c.createUser),
|
||||||
|
Route.new(.GET, "/users/:id", c.getUser),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,8 +45,9 @@ pub const RequestServer = struct {
|
||||||
defer srv.shutdown();
|
defer srv.shutdown();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var buf: [1 << 20]u8 = undefined;
|
const buf = try self.alloc.alloc(u8, 1 << 28); // 4mb
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
defer self.alloc.free(buf);
|
||||||
|
var fba = std.heap.FixedBufferAllocator.init(buf);
|
||||||
const alloc = fba.allocator();
|
const alloc = fba.allocator();
|
||||||
|
|
||||||
var ctx = try srv.accept(alloc);
|
var ctx = try srv.accept(alloc);
|
||||||
|
|
|
@ -15,6 +15,8 @@ pub const Note = struct {
|
||||||
pub const User = struct {
|
pub const User = struct {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
handle: []const u8,
|
handle: []const u8,
|
||||||
|
|
||||||
|
hashed_password: []const u8, // encoded in PHC format, with salt
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Reaction = struct {
|
pub const Reaction = struct {
|
||||||
|
|
Loading…
Reference in a new issue