1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
const std = @import("std");
const zqlite = @import("zqlite");
const bcrypt = std.crypto.pwhash.bcrypt;
const rand = std.crypto.random;
const token_length: usize = 20;
pub const User = struct {
email: []const u8,
name: []const u8,
};
pub fn get_user(allocator: std.mem.Allocator, conn: zqlite.Conn, login_token: []const u8) !?User {
const query =
\\SELECT email, name
\\FROM users
\\WHERE login_token = ?
;
if (try conn.row(query, .{login_token})) |row| {
defer row.deinit();
return User{
.email = try allocator.dupe(u8, row.text(0)),
.name = try allocator.dupe(u8, row.text(1)),
};
} else {
return null;
}
}
pub fn check_password(allocator: std.mem.Allocator, conn: zqlite.Conn, email: []const u8, password: []const u8) !?User {
const query =
\\SELECT password_hash, email, name
\\FROM users
\\WHERE email = ?
;
if (try conn.row(query, .{email})) |row| {
defer row.deinit();
const hash = row.text(0);
const verify_options = bcrypt.VerifyOptions{ .silently_truncate_password = false };
std.time.sleep(100000000 + rand.intRangeAtMost(u32, 0, 100000000));
bcrypt.strVerify(hash, password, verify_options) catch {
return null;
};
return User{
.email = try allocator.dupe(u8, row.text(1)),
.name = try allocator.dupe(u8, row.text(2)),
};
} else {
std.time.sleep(500000000 + rand.intRangeAtMost(u32, 0, 500000000));
return null;
}
}
pub fn generate_login_token(allocator: std.mem.Allocator, conn: zqlite.Conn, email: []const u8) ![]const u8 {
const query =
\\UPDATE users
\\SET login_token = ?, updated_at = datetime()
\\WHERE email = ?
;
const token = try random_token(allocator);
try conn.exec(query, .{ token, email });
return token;
}
fn random_token(allocator: std.mem.Allocator) ![]const u8 {
// Generate random bytes
var bytes: [token_length]u8 = undefined;
rand.bytes(&bytes);
// Encode as base64
const Encoder = std.base64.standard.Encoder;
const encoded_length = Encoder.calcSize(token_length);
const token = try allocator.alloc(u8, encoded_length);
_ = Encoder.encode(token, &bytes);
return token;
}
pub fn remove_login_token(conn: zqlite.Conn, email: []const u8) !void {
const query =
\\ UPDATE users
\\ SET login_token = NULL, updated_at = datetime()
\\ WHERE email = ?
;
try conn.exec(query, .{email});
}
|