Refactor passkey and token processing for improved consistency and validation

- Update passkey data mapping to use associative arrays and handle JSON decoding for `transports`.
- Refactor token caching logic to validate database existence and clear stale entries.
- Improve 2FA handling by centralizing `token->type` and `user_id` processing for reuse.
This commit is contained in:
Jeppe Bundgaard
2026-02-23 22:44:06 +01:00
parent 8ae0e28162
commit 63c88a463d
3 changed files with 41 additions and 19 deletions
+17 -4
View File
@@ -23,7 +23,15 @@ class tokens_o extends db
public function objectChanged(): void
{
// Invalidate the cache
redis->clear_token($this->token->value());
if ($this->id > 0) {
try {
$token = $this->token->value();
redis->clear_token($token);
} catch (\Exception $e) {
// If the record is already gone or token cannot be retrieved, we can't clear by token string.
// This can happen during a hard delete where objectChanged is called after the record is deleted.
}
}
}
public function create(int $user_id, string $token, string $type = 'AUTH_TOKEN'): void
@@ -58,9 +66,14 @@ class tokens_o extends db
// Check if the token is cached
$cached = redis->get_token($token);
if ($cached) {
$this->id = $cached['id'];
$this->getObjectProperties();
return $this;
$this->id = (int)$cached['id'];
// Verify that the token still exists in the database to avoid stale cache issues.
if ($this->exists()) {
$this->getObjectProperties();
return $this;
}
// If it doesn't exist, clear the stale cache entry and proceed to check the database.
redis->clear_token($token);
}
// Avoid SQL injection
$token = $db->escape_string($token);
+8 -5
View File
@@ -189,15 +189,18 @@ class authRoute
}
$auth = new authentication();
if ($token->type->value() === '2FA_VERIFICATION_USER') {
$user = (new users_o())->getUserById($token->user_id->value());
$token_type = $token->type->value();
$user_id = (int)$token->user_id->value();
if ($token_type === '2FA_VERIFICATION_USER') {
$user = (new users_o())->getUserById($user_id);
if ($user->exists() && $auth->verify_2fa_code($user, $code)) {
$token_o->delete($token_str);
$new_token = $auth->create_employee_token($token->user_id->value()); // Works for both users and employees
$new_token = $auth->create_employee_token($user_id); // Works for both users and employees
$response->success(['token' => $new_token]);
}
} elseif ($token->type->value() === '2FA_VERIFICATION_SUBUSER') {
$subuser = (new subusers_o())->select($token->user_id->value());
} elseif ($token_type === '2FA_VERIFICATION_SUBUSER') {
$subuser = (new subusers_o())->select($user_id);
if ($auth->verify_2fa_code($subuser, $code)) {
$token_o->delete($token_str);
$new_token = $subuser->generateSession();
+16 -10
View File
@@ -25,17 +25,23 @@ class passkeysRoute
$passkeys = new passkeys_o();
// Restrict to current user (customer) and non-subuser records
$passkeys->setAdditionalWhereClause('WHERE user_id = ' . (int)$user->id . ' AND is_subuser = 0 AND deleted_at IS NULL');
$list = $passkeys->listObjects(function ($o) {
$passkeys->setAdditionalWhereClause('`user_id` = ' . (int)$user->id . ' AND `is_subuser` = 0');
$list = $passkeys->listObjectsWithPaginationIfSet(function ($o) {
// $o is an associative array from the database
$transports = null;
if (isset($o['transports'])) {
$decoded = json_decode($o['transports'], true);
$transports = is_array($decoded) ? $decoded : null;
}
return [
'id' => $o->id,
'credential_id' => $o->credential_id->value(),
'name' => $o->name->value(),
'algorithm' => $o->algorithm->value(),
'transports' => $o->transports->value(),
'sign_count' => $o->sign_count->value(),
'created_at' => $o->created_at->value(),
'updated_at' => $o->updated_at->value(),
'id' => isset($o['id']) ? (int)$o['id'] : null,
'credential_id' => $o['credential_id'] ?? null,
'name' => $o['name'] ?? null,
'algorithm' => $o['algorithm'] ?? null,
'transports' => $transports,
'sign_count' => isset($o['sign_count']) ? (int)$o['sign_count'] : null,
'created_at' => $o['created_at'] ?? null,
'updated_at' => $o['updated_at'] ?? null,
];
});