feat: sign in with passkeys, no password once an account has one; an account button and dialog in place of the sign-out button
This commit is contained in:
1 parent
d2d6fe0100
commit
e7b5d28470
9 files changed
+892
-18
No files matched your search
@@ -85,6 +85,16 @@ CREATE TABLE IF NOT EXISTS ufw_rules (
|
||||
created_at INTEGER NOT NULL,
|
||||
UNIQUE(port, proto)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS admin_keys (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
admin_id INTEGER NOT NULL REFERENCES admins(id) ON DELETE CASCADE,
|
||||
cred_id BLOB UNIQUE NOT NULL,
|
||||
public_key BLOB NOT NULL,
|
||||
sign_count INTEGER NOT NULL DEFAULT 0,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
created_at INTEGER NOT NULL,
|
||||
last_used INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS forward_rules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
agent_id TEXT NOT NULL,
|
||||
@@ -188,6 +198,89 @@ func (db *DB) DeleteAdmin(id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// AdminKey is a passkey an admin signs in with.
|
||||
type AdminKey struct {
|
||||
ID int64
|
||||
AdminID int64
|
||||
CredID []byte
|
||||
PublicKey []byte // PKIX DER
|
||||
SignCount uint32
|
||||
Name string
|
||||
CreatedAt int64
|
||||
LastUsed int64
|
||||
}
|
||||
|
||||
func (db *DB) AdminByID(id int64) (*Admin, error) {
|
||||
a := &Admin{}
|
||||
err := db.sql.QueryRow(`SELECT id, username, pass_hash FROM admins WHERE id = ?`, id).
|
||||
Scan(&a.ID, &a.Username, &a.PassHash)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return a, err
|
||||
}
|
||||
|
||||
func (db *DB) AddAdminKey(k AdminKey) error {
|
||||
_, err := db.sql.Exec(
|
||||
`INSERT INTO admin_keys(admin_id, cred_id, public_key, sign_count, name, created_at) VALUES(?, ?, ?, ?, ?, ?)`,
|
||||
k.AdminID, k.CredID, k.PublicKey, k.SignCount, k.Name, time.Now().Unix())
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) ListAdminKeys(adminID int64) ([]AdminKey, error) {
|
||||
rows, err := db.sql.Query(
|
||||
`SELECT id, admin_id, cred_id, public_key, sign_count, name, created_at, last_used
|
||||
FROM admin_keys WHERE admin_id = ? ORDER BY id`, adminID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []AdminKey
|
||||
for rows.Next() {
|
||||
var k AdminKey
|
||||
if err := rows.Scan(&k.ID, &k.AdminID, &k.CredID, &k.PublicKey, &k.SignCount, &k.Name, &k.CreatedAt, &k.LastUsed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, k)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// CountAdminKeys is how many passkeys an admin has: with any, the password
|
||||
// no longer signs them in.
|
||||
func (db *DB) CountAdminKeys(adminID int64) int {
|
||||
var n int
|
||||
_ = db.sql.QueryRow(`SELECT COUNT(*) FROM admin_keys WHERE admin_id = ?`, adminID).Scan(&n)
|
||||
return n
|
||||
}
|
||||
|
||||
func (db *DB) AdminKeyByCredID(credID []byte) (*AdminKey, error) {
|
||||
k := &AdminKey{}
|
||||
err := db.sql.QueryRow(
|
||||
`SELECT id, admin_id, cred_id, public_key, sign_count, name, created_at, last_used
|
||||
FROM admin_keys WHERE cred_id = ?`, credID).
|
||||
Scan(&k.ID, &k.AdminID, &k.CredID, &k.PublicKey, &k.SignCount, &k.Name, &k.CreatedAt, &k.LastUsed)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return k, err
|
||||
}
|
||||
|
||||
func (db *DB) UseAdminKey(id int64, signCount uint32) error {
|
||||
_, err := db.sql.Exec(`UPDATE admin_keys SET sign_count = ?, last_used = ? WHERE id = ?`, signCount, time.Now().Unix(), id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) DeleteAdminKey(adminID, id int64) error {
|
||||
_, err := db.sql.Exec(`DELETE FROM admin_keys WHERE admin_id = ? AND id = ?`, adminID, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) DeleteAdminKeys(adminID int64) error {
|
||||
_, err := db.sql.Exec(`DELETE FROM admin_keys WHERE admin_id = ?`, adminID)
|
||||
return err
|
||||
}
|
||||
|
||||
// --- admin sessions ---
|
||||
|
||||
func (db *DB) CreateSession(id string, adminID int64, ttl time.Duration) error {
|
||||
|
||||
Reference in new issue
Block a user