diff --git a/VERSION b/VERSION
index 797af288..1041049a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-20250422.3
+20250423
diff --git a/database/2025_04_23_224653_make_oauth_stuff_bigint.php b/database/2025_04_23_224653_make_oauth_stuff_bigint.php
new file mode 100644
index 00000000..4f047483
--- /dev/null
+++ b/database/2025_04_23_224653_make_oauth_stuff_bigint.php
@@ -0,0 +1,169 @@
+<?php
+use Index\Db\DbConnection;
+use Index\Db\Migration\DbMigration;
+
+final class MakeOauthStuffBigint_20250423_224653 implements DbMigration {
+    public function migrate(DbConnection $conn): void {
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_scopes
+                DROP FOREIGN KEY apps_scopes_app_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_scopes
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NOT NULL FIRST;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_uris
+                DROP FOREIGN KEY apps_uris_app_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_uris
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NOT NULL AFTER uri_id;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_access
+                DROP FOREIGN KEY oauth2_access_app_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_access
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NULL DEFAULT NULL AFTER acc_id;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_authorise
+                DROP FOREIGN KEY oauth2_authorise_app_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_authorise
+                DROP FOREIGN KEY oauth2_authorise_uri_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_authorise
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NOT NULL AFTER auth_id,
+                CHANGE COLUMN uri_id uri_id BIGINT UNSIGNED NOT NULL AFTER user_id;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_device
+                DROP FOREIGN KEY oauth2_device_app_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_device
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NOT NULL AFTER dev_id;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_refresh
+                DROP FOREIGN KEY oauth2_refresh_app_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_refresh
+                DROP FOREIGN KEY oauth2_refresh_access_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_refresh
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NULL DEFAULT NULL AFTER ref_id,
+                CHANGE COLUMN acc_id acc_id BIGINT UNSIGNED NULL DEFAULT NULL AFTER user_id;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps
+                CHANGE COLUMN app_id app_id BIGINT UNSIGNED NOT NULL FIRST,
+                DROP COLUMN app_client_id,
+                DROP INDEX apps_client_id_unique;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_uris
+                CHANGE COLUMN uri_id uri_id BIGINT UNSIGNED NOT NULL FIRST;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_access
+                CHANGE COLUMN acc_id acc_id BIGINT UNSIGNED NOT NULL FIRST;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_authorise
+                CHANGE COLUMN auth_id auth_id BIGINT UNSIGNED NOT NULL FIRST;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_device
+                CHANGE COLUMN dev_id dev_id BIGINT UNSIGNED NOT NULL FIRST;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_refresh
+                CHANGE COLUMN ref_id ref_id BIGINT UNSIGNED NOT NULL FIRST;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_scopes
+                ADD CONSTRAINT apps_scopes_app_foreign
+                FOREIGN KEY (app_id)
+                REFERENCES msz_apps (app_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_apps_uris
+                ADD CONSTRAINT apps_uris_app_foreign
+                FOREIGN KEY (app_id)
+                REFERENCES msz_apps (app_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_access
+                ADD CONSTRAINT oauth2_access_app_foreign
+                FOREIGN KEY (app_id)
+                REFERENCES msz_apps (app_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_authorise
+                ADD CONSTRAINT oauth2_authorise_app_foreign
+                FOREIGN KEY (app_id)
+                REFERENCES msz_apps (app_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_authorise
+                ADD CONSTRAINT oauth2_authorise_uri_foreign
+                FOREIGN KEY (uri_id)
+                REFERENCES msz_apps_uris (uri_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_device
+                ADD CONSTRAINT oauth2_device_app_foreign
+                FOREIGN KEY (app_id)
+                REFERENCES msz_apps (app_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_oauth2_refresh
+                ADD CONSTRAINT oauth2_refresh_app_foreign
+                FOREIGN KEY (app_id)
+                REFERENCES msz_apps (app_id)
+                ON UPDATE CASCADE
+                ON DELETE CASCADE;
+        SQL);
+
+        $snowflake = new \Index\Snowflake\RandomSnowflake;
+        $stmt = $conn->prepare('UPDATE msz_apps SET app_id = ? WHERE app_id = ?');
+        $result = $conn->query('SELECT app_id, UNIX_TIMESTAMP(app_created) FROM msz_apps');
+        while($result->next()) {
+            $stmt->addParameter(1, (string)$snowflake->next(at: $result->getInteger(1) * 1000));
+            $stmt->addParameter(2, $result->getString(0));
+            $stmt->execute();
+        }
+    }
+}
diff --git a/public-legacy/settings/data.php b/public-legacy/settings/data.php
index 04b280f1..fd51de68 100644
--- a/public-legacy/settings/data.php
+++ b/public-legacy/settings/data.php
@@ -128,7 +128,7 @@ if(isset($_POST['action']) && is_string($_POST['action'])) {
                         $tmpFiles = [];
 
                         try {
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'apps',                   ['app_id:s', 'user_id:s:n', 'app_name:s', 'app_summary:s', 'app_website:s', 'app_type:s', 'app_access_lifetime:i:n', 'app_refresh_lifetime:i:n', 'app_client_id:s', 'app_client_secret:n', 'app_created:t', 'app_updated:t', 'app_deleted:t:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'apps',                   ['app_id:s', 'user_id:s:n', 'app_name:s', 'app_summary:s', 'app_website:s', 'app_type:s', 'app_access_lifetime:i:n', 'app_refresh_lifetime:i:n', 'app_client_secret:n', 'app_created:t', 'app_updated:t', 'app_deleted:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'audit_log',              ['log_id:i', 'user_id:s:n', 'log_action:s', 'log_params:j', 'log_created:t', 'log_remote_addr:a:n', 'log_country:s']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'auth_tfa',               ['user_id:s', 'tfa_token:n', 'tfa_created:t']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'changelog_changes',      ['change_id:s', 'user_id:s:n', 'change_action:s:n', 'change_created:t', 'change_log:s', 'change_text:s:n']);
diff --git a/src/Apps/AppInfo.php b/src/Apps/AppInfo.php
index 16f760e2..98c91cb0 100644
--- a/src/Apps/AppInfo.php
+++ b/src/Apps/AppInfo.php
@@ -14,7 +14,6 @@ class AppInfo {
         public private(set) AppType $type,
         public private(set) ?int $accessTokenLifetime,
         public private(set) ?int $refreshTokenLifetime,
-        public private(set) string $clientId,
         #[\SensitiveParameter] private string $clientSecret,
         public private(set) int $createdTime,
         public private(set) int $updatedTime,
@@ -31,11 +30,10 @@ class AppInfo {
             type: AppType::tryFrom($result->getString(5)) ?? AppType::Public,
             accessTokenLifetime: $result->getIntegerOrNull(6),
             refreshTokenLifetime: $result->getIntegerOrNull(7),
-            clientId: $result->getString(8),
-            clientSecret: $result->getString(9),
-            createdTime: $result->getInteger(10),
-            updatedTime: $result->getInteger(11),
-            deletedTime: $result->getIntegerOrNull(12),
+            clientSecret: $result->getString(8),
+            createdTime: $result->getInteger(9),
+            updatedTime: $result->getInteger(10),
+            deletedTime: $result->getIntegerOrNull(11),
         );
     }
 
diff --git a/src/Apps/AppsContext.php b/src/Apps/AppsContext.php
index 813942fb..ca49d440 100644
--- a/src/Apps/AppsContext.php
+++ b/src/Apps/AppsContext.php
@@ -3,13 +3,17 @@ namespace Misuzu\Apps;
 
 use RuntimeException;
 use Index\Db\DbConnection;
+use Index\Snowflake\RandomSnowflake;
 
 class AppsContext {
     public private(set) AppsData $apps;
     public private(set) ScopesData $scopes;
 
-    public function __construct(DbConnection $dbConn) {
-        $this->apps = new AppsData($dbConn);
+    public function __construct(
+        DbConnection $dbConn,
+        RandomSnowflake $snowflake,
+    ) {
+        $this->apps = new AppsData($dbConn, $snowflake);
         $this->scopes = new ScopesData($dbConn);
     }
 
@@ -38,7 +42,7 @@ class AppsContext {
         bool $breakOnFail = true
     ): array {
         if(is_string($appInfo))
-            $appInfo = $this->apps->getAppInfo(appId: $appInfo, deleted: false);
+            $appInfo = $this->apps->getAppInfo($appInfo, deleted: false);
 
         $infos = [];
 
diff --git a/src/Apps/AppsData.php b/src/Apps/AppsData.php
index c4e307dc..fcb744a2 100644
--- a/src/Apps/AppsData.php
+++ b/src/Apps/AppsData.php
@@ -6,40 +6,38 @@ use InvalidArgumentException;
 use RuntimeException;
 use Index\XString;
 use Index\Db\{DbConnection,DbStatementCache};
+use Index\Snowflake\RandomSnowflake;
 use Misuzu\Users\UserInfo;
 
 class AppsData {
     private DbStatementCache $cache;
 
-    public function __construct(DbConnection $dbConn) {
+    public function __construct(
+        DbConnection $dbConn,
+        private RandomSnowflake $snowflake,
+    ) {
         $this->cache = new DbStatementCache($dbConn);
     }
 
     public function getAppInfo(
-        ?string $appId = null,
-        ?string $clientId = null,
-        ?bool $deleted = null
+        string $appId,
+        ?bool $deleted = null,
     ): AppInfo {
-        $hasAppId = $appId !== null;
-        $hasClientId = $clientId !== null;
         $hasDeleted = $deleted !== null;
 
-        if($hasAppId === $hasClientId)
-            throw new InvalidArgumentException('you must specify either $appId or $clientId');
-
         $values = [];
         $query = <<<SQL
             SELECT app_id, user_id, app_name, app_summary, app_website, app_type,
-                app_access_lifetime, app_refresh_lifetime, app_client_id, app_client_secret,
+                app_access_lifetime, app_refresh_lifetime, app_client_secret,
                 UNIX_TIMESTAMP(app_created), UNIX_TIMESTAMP(app_updated), UNIX_TIMESTAMP(app_deleted)
             FROM msz_apps
+            WHERE app_id = ?
         SQL;
-        $query .= sprintf(' WHERE %s = ?', $hasAppId ? 'app_id' : 'app_client_id');
         if($hasDeleted)
             $query .= sprintf(' AND app_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
 
         $stmt = $this->cache->get($query);
-        $stmt->nextParameter($hasAppId ? $appId : $clientId);
+        $stmt->nextParameter($appId);
         $stmt->execute();
 
         $result = $stmt->getResult();
@@ -58,7 +56,6 @@ class AppsData {
         UserInfo|string|null $userInfo = null,
         string $summary = '',
         string $website = '',
-        ?string $clientId = null,
         #[\SensitiveParameter] ?string $clientSecret = null,
         ?int $accessLifetime = null,
         ?int $refreshLifetime = null
@@ -66,11 +63,6 @@ class AppsData {
         if(trim($name) === '')
             throw new InvalidArgumentException('$name may not be empty');
 
-        if($clientId === null)
-            $clientId = XString::random(self::CLIENT_ID_LENGTH);
-        elseif(trim($clientId) === '')
-            throw new InvalidArgumentException('$clientId may not be empty');
-
         if($accessLifetime !== null && $accessLifetime < 1)
             throw new InvalidArgumentException('$accessLifetime must be null or greater than zero');
         if($refreshLifetime !== null && $refreshLifetime < 0)
@@ -83,13 +75,14 @@ class AppsData {
         if($type !== AppType::Public && $clientSecret === '')
             throw new InvalidArgumentException('$clientSecret must be specified for confidential clients');
 
+        $appId = (string)$this->snowflake->next();
         $stmt = $this->cache->get(<<<SQL
             INSERT INTO msz_apps (
-                user_id, app_name, app_summary, app_website, app_type,
-                app_access_lifetime, app_refresh_lifetime,
-                app_client_id, app_client_secret
+                app_id, user_id, app_name, app_summary, app_website, app_type,
+                app_access_lifetime, app_refresh_lifetime, app_client_secret
             ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
         SQL);
+        $stmt->nextParameter($appId);
         $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
         $stmt->nextParameter($name);
         $stmt->nextParameter($summary);
@@ -97,11 +90,10 @@ class AppsData {
         $stmt->nextParameter($type);
         $stmt->nextParameter($accessLifetime);
         $stmt->nextParameter($refreshLifetime);
-        $stmt->nextParameter($clientId);
         $stmt->nextParameter($clientSecret);
         $stmt->execute();
 
-        return $this->getAppInfo(appId: (string)$stmt->lastInsertId);
+        return $this->getAppInfo($appId);
     }
 
     public function countAppUris(AppInfo|string $appInfo): int {
diff --git a/src/Auth/AuthProcessors.php b/src/Auth/AuthProcessors.php
index 1c49c92f..3999e65a 100644
--- a/src/Auth/AuthProcessors.php
+++ b/src/Auth/AuthProcessors.php
@@ -358,7 +358,7 @@ final class AuthProcessors implements RouteHandler {
             } else return false;
 
             try {
-                $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(clientId: $clientId, deleted: false);
+                $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo($clientId, deleted: false);
             } catch(RuntimeException $ex) {
                 return false;
             }
diff --git a/src/OAuth2/OAuth2AuthorisationData.php b/src/OAuth2/OAuth2AuthorisationData.php
index eac0fb74..06a475f0 100644
--- a/src/OAuth2/OAuth2AuthorisationData.php
+++ b/src/OAuth2/OAuth2AuthorisationData.php
@@ -5,6 +5,7 @@ use InvalidArgumentException;
 use RuntimeException;
 use Index\XString;
 use Index\Db\{DbConnection,DbStatementCache};
+use Index\Snowflake\RandomSnowflake;
 use Misuzu\Apps\{AppInfo,AppUriInfo};
 use Misuzu\Users\UserInfo;
 
@@ -12,7 +13,8 @@ class OAuth2AuthorisationData {
     private DbStatementCache $cache;
 
     public function __construct(
-        private DbConnection $dbConn
+        private DbConnection $dbConn,
+        private RandomSnowflake $snowflake,
     ) {
         $this->cache = new DbStatementCache($dbConn);
     }
@@ -68,13 +70,15 @@ class OAuth2AuthorisationData {
         string $scope,
         ?string $code = null
     ): OAuth2AuthorisationInfo {
+        $authId = (string)$this->snowflake->next();
         $code ??= XString::random(60);
 
         $stmt = $this->cache->get(<<<SQL
             INSERT INTO msz_oauth2_authorise (
-                app_id, user_id, uri_id, auth_challenge_code, auth_challenge_method, auth_scope, auth_code
-            ) VALUES (?, ?, ?, ?, ?, ?, ?)
+                auth_id, app_id, user_id, uri_id, auth_challenge_code, auth_challenge_method, auth_scope, auth_code
+            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
         SQL);
+        $stmt->nextParameter($authId);
         $stmt->nextParameter($appInfo instanceof AppInfo ? $appInfo->id : $appInfo);
         $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
         $stmt->nextParameter($appUriInfo instanceof AppUriInfo ? $appUriInfo->id : $appUriInfo);
@@ -84,7 +88,7 @@ class OAuth2AuthorisationData {
         $stmt->nextParameter($code);
         $stmt->execute();
 
-        return $this->getAuthorisationInfo(authsId: (string)$stmt->lastInsertId);
+        return $this->getAuthorisationInfo(authsId: $authId);
     }
 
     public function deleteAuthorisation(
diff --git a/src/OAuth2/OAuth2Context.php b/src/OAuth2/OAuth2Context.php
index 865e096e..7658b1fc 100644
--- a/src/OAuth2/OAuth2Context.php
+++ b/src/OAuth2/OAuth2Context.php
@@ -4,6 +4,7 @@ namespace Misuzu\OAuth2;
 use RuntimeException;
 use Index\Config\Config;
 use Index\Db\DbConnection;
+use Index\Snowflake\RandomSnowflake;
 use Misuzu\SiteInfo;
 use Misuzu\Apps\{AppsContext,AppInfo};
 use Misuzu\JWT\JWT;
@@ -46,12 +47,13 @@ class OAuth2Context {
     public function __construct(
         private Config $config,
         DbConnection $dbConn,
+        RandomSnowflake $snowflake,
         public private(set) AppsContext $appsCtx,
         private SiteInfo $siteInfo,
     ) {
-        $this->authorisations = new OAuth2AuthorisationData($dbConn);
-        $this->tokens = new OAuth2TokensData($dbConn);
-        $this->devices = new OAuth2DevicesData($dbConn);
+        $this->authorisations = new OAuth2AuthorisationData($dbConn, $snowflake);
+        $this->tokens = new OAuth2TokensData($dbConn, $snowflake);
+        $this->devices = new OAuth2DevicesData($dbConn, $snowflake);
         $this->keys = new OAuth2Keys($config->getArray('keys'));
     }
 
@@ -147,7 +149,7 @@ class OAuth2Context {
         $token = [
             'iss' => $siteInfo->url,
             'sub' => $accessOrAuthzInfo->userId,
-            'aud' => $appInfo->clientId,
+            'aud' => $appInfo->id,
             'exp' => $accessOrAuthzInfo->expiresTime,
             'iat' => $issuedAt ?? time(),
             'auth_time' => $accessOrAuthzInfo->createdTime,
diff --git a/src/OAuth2/OAuth2DevicesData.php b/src/OAuth2/OAuth2DevicesData.php
index 05d049c2..884a9775 100644
--- a/src/OAuth2/OAuth2DevicesData.php
+++ b/src/OAuth2/OAuth2DevicesData.php
@@ -5,6 +5,7 @@ use InvalidArgumentException;
 use RuntimeException;
 use Index\XString;
 use Index\Db\{DbConnection,DbStatementCache};
+use Index\Snowflake\RandomSnowflake;
 use Misuzu\Apps\AppInfo;
 use Misuzu\Users\UserInfo;
 
@@ -14,7 +15,8 @@ class OAuth2DevicesData {
     private DbStatementCache $cache;
 
     public function __construct(
-        private DbConnection $dbConn
+        private DbConnection $dbConn,
+        private RandomSnowflake $snowflake,
     ) {
         $this->cache = new DbStatementCache($dbConn);
     }
@@ -82,14 +84,16 @@ class OAuth2DevicesData {
         ?string $code = null,
         ?string $userCode = null
     ): OAuth2DeviceInfo {
+        $devId = (string)$this->snowflake->next();
         $code ??= XString::random(60);
         $userCode ??= XString::random(9, self::USER_CODE_CHARS);
 
         $stmt = $this->cache->get(<<<SQL
             INSERT INTO msz_oauth2_device (
-                app_id, user_id, dev_code, dev_user_code, dev_scope
-            ) VALUES (?, ?, ?, ?, ?)
+                dev_id, app_id, user_id, dev_code, dev_user_code, dev_scope
+            ) VALUES (?, ?, ?, ?, ?, ?)
         SQL);
+        $stmt->nextParameter($devId);
         $stmt->nextParameter($appInfo instanceof AppInfo ? $appInfo->id : $appInfo);
         $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
         $stmt->nextParameter($code);
@@ -97,7 +101,7 @@ class OAuth2DevicesData {
         $stmt->nextParameter($scope);
         $stmt->execute();
 
-        return $this->getDeviceInfo(deviceId: (string)$stmt->lastInsertId);
+        return $this->getDeviceInfo(deviceId: $devId);
     }
 
     public function deleteDevice(
diff --git a/src/OAuth2/OAuth2TokensData.php b/src/OAuth2/OAuth2TokensData.php
index d7b39942..7ba1e83b 100644
--- a/src/OAuth2/OAuth2TokensData.php
+++ b/src/OAuth2/OAuth2TokensData.php
@@ -5,6 +5,7 @@ use InvalidArgumentException;
 use RuntimeException;
 use Index\XString;
 use Index\Db\{DbConnection,DbStatementCache};
+use Index\Snowflake\RandomSnowflake;
 use Misuzu\Apps\AppInfo;
 use Misuzu\Users\UserInfo;
 
@@ -12,7 +13,8 @@ class OAuth2TokensData {
     private DbStatementCache $cache;
 
     public function __construct(
-        private DbConnection $dbConn
+        private DbConnection $dbConn,
+        private RandomSnowflake $snowflake,
     ) {
         $this->cache = new DbStatementCache($dbConn);
     }
@@ -48,17 +50,19 @@ class OAuth2TokensData {
         string $scope = '',
         ?int $lifetime = null
     ): OAuth2AccessInfo {
+        $accId = (string)$this->snowflake->next();
         $token ??= XString::random(80);
 
         $stmt = $this->cache->get(<<<SQL
             INSERT INTO msz_oauth2_access (
-                app_id, user_id, acc_token, acc_scope,
+                acc_id, app_id, user_id, acc_token, acc_scope,
                 acc_expires
             ) VALUES (
-                ?, ?, ?, ?,
+                ?, ?, ?, ?, ?,
                 IF(?, NOW() + INTERVAL ? SECOND, DEFAULT(acc_expires))
             )
         SQL);
+        $stmt->nextParameter($accId);
         $stmt->nextParameter($appInfo instanceof AppInfo ? $appInfo->id : $appInfo);
         $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
         $stmt->nextParameter($token);
@@ -67,7 +71,7 @@ class OAuth2TokensData {
         $stmt->nextParameter($lifetime);
         $stmt->execute();
 
-        return $this->getAccessInfo((string)$stmt->lastInsertId, OAuth2AccessInfoGetField::Id);
+        return $this->getAccessInfo($accId, OAuth2AccessInfoGetField::Id);
     }
 
     public function deleteAccess(
@@ -149,17 +153,19 @@ class OAuth2TokensData {
         string $scope = '',
         ?int $lifetime = null
     ): OAuth2RefreshInfo {
+        $refId = (string)$this->snowflake->next();
         $token ??= XString::random(120);
 
         $stmt = $this->cache->get(<<<SQL
             INSERT INTO msz_oauth2_refresh (
-                app_id, user_id, acc_id, ref_token, ref_scope,
+                ref_id, app_id, user_id, acc_id, ref_token, ref_scope,
                 ref_expires
             ) VALUES (
-                ?, ?, ?, ?, ?,
+                ?, ?, ?, ?, ?, ?,
                 IF(?, NOW() + INTERVAL ? SECOND, DEFAULT(ref_expires))
             )
         SQL);
+        $stmt->nextParameter($refId);
         $stmt->nextParameter($appInfo instanceof AppInfo ? $appInfo->id : $appInfo);
         $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
         $stmt->nextParameter($accessInfo instanceof OAuth2AccessInfo ? $accessInfo->id : $accessInfo);
@@ -169,7 +175,7 @@ class OAuth2TokensData {
         $stmt->nextParameter($lifetime);
         $stmt->execute();
 
-        return $this->getRefreshInfo((string)$stmt->lastInsertId, OAuth2RefreshInfoGetField::Id);
+        return $this->getRefreshInfo($refId, OAuth2RefreshInfoGetField::Id);
     }
 
     public function deleteRefresh(
diff --git a/src/OAuth2/OAuth2WebRoutes.php b/src/OAuth2/OAuth2WebRoutes.php
index e3387a3f..055f5265 100644
--- a/src/OAuth2/OAuth2WebRoutes.php
+++ b/src/OAuth2/OAuth2WebRoutes.php
@@ -85,7 +85,7 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
 
         try {
             $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(
-                clientId: (string)$content->getParam('client'),
+                (string)$content->getParam('client'),
                 deleted: false
             );
         } catch(RuntimeException $ex) {
@@ -189,7 +189,7 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
 
         try {
             $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(
-                clientId: (string)$request->getParam('client'),
+                (string)$request->getParam('client'),
                 deleted: false
             );
         } catch(RuntimeException $ex) {
@@ -302,7 +302,7 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
         if($approved) {
             try {
                 $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(
-                    appId: $deviceInfo->appId,
+                    $deviceInfo->appId,
                     deleted: false
                 );
             } catch(RuntimeException $ex) {
@@ -390,7 +390,7 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
 
         try {
             $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(
-                appId: $deviceInfo->appId,
+                $deviceInfo->appId,
                 deleted: false
             );
         } catch(RuntimeException $ex) {