diff --git a/database/2024_07_27_025524_configurable_refresh_lifetime.php b/database/2024_07_27_025524_configurable_refresh_lifetime.php new file mode 100644 index 0000000..1e820d4 --- /dev/null +++ b/database/2024_07_27_025524_configurable_refresh_lifetime.php @@ -0,0 +1,9 @@ +execute('ALTER TABLE hau_apps ADD COLUMN app_refresh_lifetime INT UNSIGNED NULL DEFAULT NULL AFTER app_type'); + } +} diff --git a/src/Apps/AppInfo.php b/src/Apps/AppInfo.php index 1d26a8f..305d2ec 100644 --- a/src/Apps/AppInfo.php +++ b/src/Apps/AppInfo.php @@ -11,6 +11,7 @@ class AppInfo { private string $website, private bool $trusted, private string $type, + private ?int $refreshLifetime, private string $clientId, private string $clientSecret, private int $created, @@ -26,11 +27,12 @@ class AppInfo { website: $result->getString(3), trusted: $result->getBoolean(4), type: $result->getString(5), - clientId: $result->getString(6), - clientSecret: $result->getString(7), - created: $result->getInteger(8), - updated: $result->getInteger(9), - deleted: $result->getIntegerOrNull(10), + refreshLifetime: $result->getIntegerOrNull(6), + clientId: $result->getString(7), + clientSecret: $result->getString(8), + created: $result->getInteger(9), + updated: $result->getInteger(10), + deleted: $result->getIntegerOrNull(11), ); } @@ -71,6 +73,16 @@ class AppInfo { return strcasecmp($this->type, 'confidential') === 0; } + public function getRefreshTokenLifetime(): ?int { + return $this->refreshLifetime; + } + public function shouldIssueRefreshToken(): bool { + if($this->refreshLifetime === null) + return $this->isConfidential(); + + return $this->refreshLifetime > 0; + } + public function getClientId(): string { return $this->clientId; } diff --git a/src/Apps/AppsData.php b/src/Apps/AppsData.php index cb93a0b..761d53f 100644 --- a/src/Apps/AppsData.php +++ b/src/Apps/AppsData.php @@ -28,7 +28,7 @@ class AppsData { throw new InvalidArgumentException('you must specify either $appId or $clientId'); $values = []; - $query = 'SELECT app_id, app_name, app_summary, app_website, app_trusted, app_type, app_client_id, app_client_secret, UNIX_TIMESTAMP(app_created), UNIX_TIMESTAMP(app_updated), UNIX_TIMESTAMP(app_deleted) FROM hau_apps'; + $query = 'SELECT app_id, app_name, app_summary, app_website, app_trusted, app_type, app_refresh_lifetime, app_client_id, app_client_secret, UNIX_TIMESTAMP(app_created), UNIX_TIMESTAMP(app_updated), UNIX_TIMESTAMP(app_deleted) FROM hau_apps'; $query .= sprintf(' WHERE %s = ?', $hasAppId ? 'app_id' : 'app_client_id'); if($hasDeleted) $query .= sprintf(' AND app_deleted %s NULL', $deleted ? 'IS NOT' : 'IS'); diff --git a/src/OAuth2/OAuth2Context.php b/src/OAuth2/OAuth2Context.php index 821c57e..b4d7fb5 100644 --- a/src/OAuth2/OAuth2Context.php +++ b/src/OAuth2/OAuth2Context.php @@ -27,18 +27,16 @@ class OAuth2Context { return $this->devices; } - public function createRefreshFromAccessInfo( - OAuth2AccessInfo $accessInfo, - ?string $token = null, - ?int $lifetime = null + public function createRefresh( + AppInfo $appInfo, + OAuth2AccessInfo $accessInfo ): OAuth2RefreshInfo { return $this->tokens->createRefresh( $accessInfo->getAppId(), $accessInfo, - $accessInfo->getUserId(), - $token, - $accessInfo->getScope(), - $lifetime + userId: $accessInfo->getUserId(), + scope: $accessInfo->getScope(), + lifetime: $appInfo->getRefreshTokenLifetime() ); } diff --git a/src/OAuth2/OAuth2Routes.php b/src/OAuth2/OAuth2Routes.php index 62c6ddc..6dd8a76 100644 --- a/src/OAuth2/OAuth2Routes.php +++ b/src/OAuth2/OAuth2Routes.php @@ -595,9 +595,8 @@ final class OAuth2Routes extends RouteHandler { if($scope === $authoriseInfo->getScope()) unset($scope); - // this should probably check something else - if($appInfo->isConfidential()) - $refreshInfo = $this->oauth2Ctx->createRefreshFromAccessInfo($accessInfo); + if($appInfo->shouldIssueRefreshToken()) + $refreshInfo = $this->oauth2Ctx->createRefresh($appInfo, $accessInfo); } elseif($type === 'refresh_token') { $tokensData = $this->oauth2Ctx->getTokensData(); try { @@ -639,9 +638,8 @@ final class OAuth2Routes extends RouteHandler { unset($refreshInfo); - // this should probably check something else - if($appInfo->isConfidential()) - $refreshInfo = $this->oauth2Ctx->createRefreshFromAccessInfo($accessInfo); + if($appInfo->shouldIssueRefreshToken()) + $refreshInfo = $this->oauth2Ctx->createRefresh($appInfo, $accessInfo); } elseif($type === 'client_credentials') { if(!$appInfo->isConfidential()) { $response->setStatusCode(400); @@ -723,9 +721,8 @@ final class OAuth2Routes extends RouteHandler { if($scope === $deviceInfo->getScope()) unset($scope); - // this should probably check something else - if($appInfo->isConfidential()) - $refreshInfo = $this->oauth2Ctx->createRefreshFromAccessInfo($accessInfo); + if($appInfo->shouldIssueRefreshToken()) + $refreshInfo = $this->oauth2Ctx->createRefresh($appInfo, $accessInfo); } else { $response->setStatusCode(400); return self::error('unsupported_grant_type', 'Requested grant type is not supported by this server.');