Allow access and refresh tokens to not be associated with an app.

This commit is contained in:
flash 2025-04-23 21:24:02 +00:00
parent d9cf9674f3
commit 093266eb1e
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
8 changed files with 70 additions and 32 deletions

View file

@ -0,0 +1,16 @@
<?php
use Index\Db\DbConnection;
use Index\Db\Migration\DbMigration;
final class AllowTokensToHaveNullApp_20250423_204653 implements DbMigration {
public function migrate(DbConnection $conn): void {
$conn->execute(<<<SQL
ALTER TABLE msz_oauth2_access
CHANGE COLUMN app_id app_id INT(10) UNSIGNED NULL DEFAULT NULL AFTER acc_id;
SQL);
$conn->execute(<<<SQL
ALTER TABLE msz_oauth2_refresh
CHANGE COLUMN app_id app_id INT(10) UNSIGNED NULL DEFAULT NULL AFTER ref_id;
SQL);
}
}

View file

@ -57,10 +57,6 @@ class AppInfo {
get => $this->type === AppType::Trusted;
}
public bool $issueRefreshToken {
get => $this->refreshTokenLifetime === null ? $this->confidential : $this->refreshTokenLifetime > 0;
}
public function verifyClientSecret(#[\SensitiveParameter] string $input): bool {
return $this->confidential && password_verify($input, $this->clientSecret);
}

View file

@ -9,7 +9,7 @@ class OAuth2AccessInfo {
public function __construct(
public private(set) string $id,
public private(set) string $appId,
public private(set) ?string $appId,
public private(set) ?string $userId,
public private(set) string $token,
public private(set) string $scope,
@ -20,7 +20,7 @@ class OAuth2AccessInfo {
public static function fromResult(DbResult $result): OAuth2AccessInfo {
return new OAuth2AccessInfo(
id: $result->getString(0),
appId: $result->getString(1),
appId: $result->getStringOrNull(1),
userId: $result->getStringOrNull(2),
token: $result->getString(3),
scope: $result->getString(4),
@ -49,4 +49,8 @@ class OAuth2AccessInfo {
public int $remainingLifetime {
get => max(0, $this->expiresTime - time());
}
public bool $issueRefreshToken {
get => in_array('offline_access', $this->scopes);
}
}

View file

@ -67,4 +67,8 @@ class OAuth2AuthorisationInfo {
public int $remainingLifetime {
get => max(0, $this->expiresTime - time());
}
public bool $issueRefreshToken {
get => in_array('offline_access', $this->scopes);
}
}

View file

@ -72,8 +72,8 @@ class OAuth2Context {
OAuth2AccessInfo $accessInfo
): OAuth2RefreshInfo {
return $this->tokens->createRefresh(
$appInfo,
$accessInfo,
$appInfo,
userInfo: $accessInfo->userId,
scope: $accessInfo->scope,
lifetime: $appInfo->refreshTokenLifetime,
@ -278,7 +278,7 @@ class OAuth2Context {
if($authsInfo->scope === $scope)
$scope = null;
$refreshInfo = $appInfo->issueRefreshToken
$refreshInfo = $authsInfo->issueRefreshToken
? $this->createRefresh($appInfo, $accessInfo)
: null;
@ -340,7 +340,7 @@ class OAuth2Context {
$scope = null;
$refreshInfo = null;
if($appInfo->issueRefreshToken)
if($accessInfo->issueRefreshToken)
$refreshInfo = $this->createRefresh($appInfo, $accessInfo);
return $this->packBearerTokenResult($appInfo, $accessInfo, $refreshInfo, $scope);
@ -448,7 +448,7 @@ class OAuth2Context {
if($deviceInfo->scope === $scope)
$scope = null;
$refreshInfo = $appInfo->issueRefreshToken
$refreshInfo = $deviceInfo->issueRefreshToken
? $this->createRefresh($appInfo, $accessInfo)
: null;

View file

@ -84,4 +84,8 @@ class OAuth2DeviceInfo {
public int $remainingLifetime {
get => max(0, $this->expiresTime - time());
}
public bool $issueRefreshToken {
get => in_array('offline_access', $this->scopes);
}
}

View file

@ -7,7 +7,7 @@ use Index\Db\DbResult;
class OAuth2RefreshInfo {
public function __construct(
public private(set) string $id,
public private(set) string $appId,
public private(set) ?string $appId,
public private(set) ?string $userId,
public private(set) ?string $accessId,
public private(set) string $token,
@ -19,7 +19,7 @@ class OAuth2RefreshInfo {
public static function fromResult(DbResult $result): OAuth2RefreshInfo {
return new OAuth2RefreshInfo(
id: $result->getString(0),
appId: $result->getString(1),
appId: $result->getStringOrNull(1),
userId: $result->getStringOrNull(2),
accessId: $result->getStringOrNull(3),
token: $result->getString(4),

View file

@ -42,7 +42,7 @@ class OAuth2TokensData {
}
public function createAccess(
AppInfo|string $appInfo,
AppInfo|string|null $appInfo = null,
UserInfo|string|null $userInfo = null,
?string $token = null,
string $scope = '',
@ -72,8 +72,8 @@ class OAuth2TokensData {
public function deleteAccess(
OAuth2AccessInfo|string|null $accessInfo = null,
AppInfo|string|null $appInfo = null,
UserInfo|string|null $userInfo = null,
AppInfo|string|null|false $appInfo = false,
UserInfo|string|null|false $userInfo = false,
): void {
$selectors = [];
$values = [];
@ -81,14 +81,20 @@ class OAuth2TokensData {
$selectors[] = 'acc_id = ?';
$values[] = $accessInfo instanceof OAuth2AccessInfo ? $accessInfo->id : $accessInfo;
}
if($appInfo !== null) {
$selectors[] = 'app_id = ?';
$values[] = $appInfo instanceof AppInfo ? $appInfo->id : $appInfo;
}
if($userInfo !== null) {
$selectors[] = 'user_id = ?';
$values[] = $userInfo instanceof UserInfo ? $userInfo->id : $userInfo;
}
if($appInfo !== false)
if($appInfo === null) {
$selectors[] = 'app_id IS NULL';
} else {
$selectors[] = 'app_id = ?';
$values[] = $appInfo instanceof AppInfo ? $appInfo->id : $appInfo;
}
if($userInfo !== false)
if($userInfo === null) {
$selectors[] = 'user_id IS NULL';
} else {
$selectors[] = 'user_id = ?';
$values[] = $userInfo instanceof UserInfo ? $userInfo->id : $userInfo;
}
$query = 'DELETE FROM msz_oauth2_access';
if(!empty($selectors))
@ -136,8 +142,8 @@ class OAuth2TokensData {
}
public function createRefresh(
AppInfo|string $appInfo,
OAuth2AccessInfo|string|null $accessInfo,
AppInfo|string|null $appInfo = null,
UserInfo|string|null $userInfo = null,
?string $token = null,
string $scope = '',
@ -168,8 +174,8 @@ class OAuth2TokensData {
public function deleteRefresh(
OAuth2RefreshInfo|string|null $refreshInfo = null,
AppInfo|string|null $appInfo = null,
UserInfo|string|null $userInfo = null,
AppInfo|string|null|false $appInfo = false,
UserInfo|string|null|false $userInfo = false,
OAuth2AccessInfo|string|null $accessInfo = null
): void {
$selectors = [];
@ -178,13 +184,21 @@ class OAuth2TokensData {
$selectors[] = 'ref_id = ?';
$values[] = $refreshInfo instanceof OAuth2RefreshInfo ? $refreshInfo->id : $refreshInfo;
}
if($appInfo !== null) {
$selectors[] = 'app_id = ?';
$values[] = $appInfo instanceof AppInfo ? $appInfo->id : $appInfo;
if($appInfo !== false) {
if($appInfo === null) {
$selectors[] = 'app_id IS NULL';
} else {
$selectors[] = 'app_id = ?';
$values[] = $appInfo instanceof AppInfo ? $appInfo->id : $appInfo;
}
}
if($userInfo !== null) {
$selectors[] = 'user_id = ?';
$values[] = $userInfo instanceof UserInfo ? $userInfo->id : $userInfo;
if($userInfo !== false) {
if($userInfo === null) {
$selectors[] = 'user_id IS NULL';
} else {
$selectors[] = 'user_id = ?';
$values[] = $userInfo instanceof UserInfo ? $userInfo->id : $userInfo;
}
}
if($accessInfo !== null) {
$selectors[] = 'acc_id = ?';