v/vlib/veb/auth/auth.v

94 lines
2.2 KiB
V

// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module auth
import rand
import crypto.rand as crypto_rand
import crypto.hmac
import crypto.sha256
const max_safe_unsigned_integer = u32(4_294_967_295)
pub struct Auth[T] {
db T
// pub:
// salt string
}
pub struct Token {
pub:
id int @[primary; sql: serial]
user_id int
value string
// ip string
}
pub fn new[T](db T) Auth[T] {
set_rand_crypto_safe_seed()
sql db {
create table Token
} or { eprintln('veb.auth: failed to create table Token') }
return Auth[T]{
db: db
// salt: generate_salt()
}
}
// fn (mut app App) add_token(user_id int, ip string) !string {
pub fn (mut app Auth[T]) add_token(user_id int) !string {
mut uuid := rand.uuid_v4()
token := Token{
user_id: user_id
value: uuid
// ip: ip
}
sql app.db {
insert token into Token
}!
return uuid
}
pub fn (app &Auth[T]) find_token(value string) ?Token {
tokens := sql app.db {
select from Token where value == value limit 1
} or { []Token{} }
if tokens.len == 0 {
return none
}
return tokens.first()
}
pub fn (mut app Auth[T]) delete_tokens(user_id int) ! {
sql app.db {
delete from Token where user_id == user_id
}!
}
pub fn set_rand_crypto_safe_seed() {
first_seed := generate_crypto_safe_int_u32()
second_seed := generate_crypto_safe_int_u32()
rand.seed([first_seed, second_seed])
}
fn generate_crypto_safe_int_u32() u32 {
return u32(crypto_rand.int_u64(max_safe_unsigned_integer) or { 0 })
}
pub fn generate_salt() string {
return rand.i64().str()
}
pub fn hash_password_with_salt(plain_text_password string, salt string) string {
salted_password := '${plain_text_password}${salt}'
return sha256.sum(salted_password.bytes()).hex().str()
}
pub fn compare_password_with_hash(plain_text_password string, salt string, hashed string) bool {
digest := hash_password_with_salt(plain_text_password, salt)
// constant time comparison
// I know this is operating on the hex-encoded strings, but it's still constant time
// and better than not doing it at all
return hmac.equal(digest.bytes(), hashed.bytes())
}