setTable('passkeys'); } /** * Finds a passkey by its credential ID, optionally constrained to a given user ID. * Returns the selected object instance on success or null if not found. */ public function findByCredentialId(string $credentialId, ?int $userId = null): ?self { global $db; $credentialId = $db->escape_string($credentialId); $where = "credential_id = '" . $credentialId . "'"; if ($userId !== null) { $where .= ' AND user_id = ' . (int)$userId; } $sql = "SELECT id FROM $this->table WHERE $where LIMIT 1"; $result = $db->query($sql); $row = $db->fetch_assoc($result); if (!$row || !isset($row['id'])) { return null; } $this->select((int)$row['id']); return $this; } /** * Add a new passkey for a user * @param int $user_id The customer number or subuser id, depending on the value of is_subuser * @param bool $is_subuser If the passkey belongs to a subuser or a customer * @param string $credential_id The credential ID of the passkey, base64url encoded * @param string $public_key The public key of the passkey, base64url encoded * @param string $algorithm The algorithm used to create the passkey, e.g. "ES256" * @param array $transports The transports supported by the authenticator, e.g. ["usb", "nfc", "ble"] * @param string|null $name The name of the passkey, e.g. "iPhone 12 Pro Max" * @return void * @throws Exception If the object was not created successfully * @throws RandomException If the token generation fails */ public function add(int $user_id, bool $is_subuser, string $credential_id, string $public_key, string $algorithm, array $transports, ?string $name = null): void { $data = [ 'user_id' => $user_id, 'is_subuser' => $is_subuser, 'credential_id' => $credential_id, 'public_key' => $public_key, 'algorithm' => $algorithm, 'transports' => json_encode($transports), 'sign_count' => 0, 'backup_state' => json_encode(new \stdClass()), 'name' => $name, ]; $tmp_id = self::add_object($data); $this->id = $tmp_id; self::getObjectProperties(); self::objectChanged(); } /** * @return void */ public function getObjectProperties(): void { $this->credential_id = new object_property($this->table, $this->id, 'credential_id', 'string', false); $this->public_key = new object_property($this->table, $this->id, 'public_key', 'string', false); $this->is_subuser = new object_property($this->table, $this->id, 'is_subuser', 'bool', false); $this->user_id = new object_property($this->table, $this->id, 'user_id', 'int', false); $this->sign_count = new object_property($this->table, $this->id, 'sign_count', 'int', false); $this->algorithm = new object_property($this->table, $this->id, 'algorithm', 'string', false); $this->transports = new object_property($this->table, $this->id, 'transports', 'json', false); $this->backup_state = new object_property($this->table, $this->id, 'backup_state', 'json', false); $this->name = new object_property($this->table, $this->id, 'name', 'string', false); $this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false); $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp', false); $this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false); } public function objectChanged(): void { //TODO: Add cache invalidation } }