Added field for specifying per app access token lifetime length.
This commit is contained in:
parent
12d48cce2e
commit
bf40942e72
4 changed files with 57 additions and 20 deletions
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
use Index\Data\IDbConnection;
|
||||||
|
use Index\Data\Migration\IDbMigration;
|
||||||
|
|
||||||
|
final class AccessLifetimeFieldForAppsTable_20240904_205008 implements IDbMigration {
|
||||||
|
public function migrate(IDbConnection $conn): void {
|
||||||
|
$conn->execute(<<<SQL
|
||||||
|
ALTER TABLE hau_apps
|
||||||
|
ADD COLUMN app_access_lifetime INT(10) UNSIGNED NULL DEFAULT NULL AFTER app_type;
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ class AppInfo {
|
||||||
private string $website,
|
private string $website,
|
||||||
private bool $trusted,
|
private bool $trusted,
|
||||||
private string $type,
|
private string $type,
|
||||||
|
private ?int $accessLifetime,
|
||||||
private ?int $refreshLifetime,
|
private ?int $refreshLifetime,
|
||||||
private string $clientId,
|
private string $clientId,
|
||||||
private string $clientSecret,
|
private string $clientSecret,
|
||||||
|
@ -27,12 +28,13 @@ class AppInfo {
|
||||||
website: $result->getString(3),
|
website: $result->getString(3),
|
||||||
trusted: $result->getBoolean(4),
|
trusted: $result->getBoolean(4),
|
||||||
type: $result->getString(5),
|
type: $result->getString(5),
|
||||||
refreshLifetime: $result->getIntegerOrNull(6),
|
accessLifetime: $result->getIntegerOrNull(6),
|
||||||
clientId: $result->getString(7),
|
refreshLifetime: $result->getIntegerOrNull(7),
|
||||||
clientSecret: $result->getString(8),
|
clientId: $result->getString(8),
|
||||||
created: $result->getInteger(9),
|
clientSecret: $result->getString(9),
|
||||||
updated: $result->getInteger(10),
|
created: $result->getInteger(10),
|
||||||
deleted: $result->getIntegerOrNull(11),
|
updated: $result->getInteger(11),
|
||||||
|
deleted: $result->getIntegerOrNull(12),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +75,10 @@ class AppInfo {
|
||||||
return strcasecmp($this->type, 'confidential') === 0;
|
return strcasecmp($this->type, 'confidential') === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAccessTokenLifetime(): ?int {
|
||||||
|
return $this->accessLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRefreshTokenLifetime(): ?int {
|
public function getRefreshTokenLifetime(): ?int {
|
||||||
return $this->refreshLifetime;
|
return $this->refreshLifetime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class AppsData {
|
||||||
throw new InvalidArgumentException('you must specify either $appId or $clientId');
|
throw new InvalidArgumentException('you must specify either $appId or $clientId');
|
||||||
|
|
||||||
$values = [];
|
$values = [];
|
||||||
$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 = 'SELECT app_id, app_name, app_summary, app_website, app_trusted, app_type, app_access_lifetime, 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');
|
$query .= sprintf(' WHERE %s = ?', $hasAppId ? 'app_id' : 'app_client_id');
|
||||||
if($hasDeleted)
|
if($hasDeleted)
|
||||||
$query .= sprintf(' AND app_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
|
$query .= sprintf(' AND app_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
|
||||||
|
@ -60,6 +60,7 @@ class AppsData {
|
||||||
string $website = '',
|
string $website = '',
|
||||||
?string $clientId = null,
|
?string $clientId = null,
|
||||||
#[\SensitiveParameter] ?string $clientSecret = null,
|
#[\SensitiveParameter] ?string $clientSecret = null,
|
||||||
|
?int $accessLifetime = null,
|
||||||
?int $refreshLifetime = null
|
?int $refreshLifetime = null
|
||||||
): AppInfo {
|
): AppInfo {
|
||||||
if(trim($name) === '')
|
if(trim($name) === '')
|
||||||
|
@ -72,6 +73,11 @@ class AppsData {
|
||||||
elseif(trim($clientId) === '')
|
elseif(trim($clientId) === '')
|
||||||
throw new InvalidArgumentException('$clientId may not be empty');
|
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)
|
||||||
|
throw new InvalidArgumentException('$refreshLifetime must be null or a positive integer');
|
||||||
|
|
||||||
$summary = trim($summary);
|
$summary = trim($summary);
|
||||||
$website = trim($website);
|
$website = trim($website);
|
||||||
$clientSecret = $clientSecret === null ? '' : password_hash($clientSecret, self::CLIENT_SECRET_ALGO);
|
$clientSecret = $clientSecret === null ? '' : password_hash($clientSecret, self::CLIENT_SECRET_ALGO);
|
||||||
|
@ -79,15 +85,16 @@ class AppsData {
|
||||||
if($type === self::TYPE_CONFIDENTIAL && $clientSecret === '')
|
if($type === self::TYPE_CONFIDENTIAL && $clientSecret === '')
|
||||||
throw new InvalidArgumentException('$clientSecret must be specified for confidential clients');
|
throw new InvalidArgumentException('$clientSecret must be specified for confidential clients');
|
||||||
|
|
||||||
$stmt = $this->cache->get('INSERT INTO hau_apps (app_name, app_summary, app_website, app_trusted, app_type, app_refresh_lifetime, app_client_id, app_client_secret) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
|
$stmt = $this->cache->get('INSERT INTO hau_apps (app_name, app_summary, app_website, app_trusted, app_type, app_access_lifetime, app_refresh_lifetime, app_client_id, app_client_secret) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
||||||
$stmt->addParameter(1, $name);
|
$stmt->addParameter(1, $name);
|
||||||
$stmt->addParameter(2, $summary);
|
$stmt->addParameter(2, $summary);
|
||||||
$stmt->addParameter(3, $website);
|
$stmt->addParameter(3, $website);
|
||||||
$stmt->addParameter(4, $trusted ? 1 : 0);
|
$stmt->addParameter(4, $trusted ? 1 : 0);
|
||||||
$stmt->addParameter(5, $type);
|
$stmt->addParameter(5, $type);
|
||||||
$stmt->addParameter(6, $refreshLifetime);
|
$stmt->addParameter(6, $accessLifetime);
|
||||||
$stmt->addParameter(7, $clientId);
|
$stmt->addParameter(7, $refreshLifetime);
|
||||||
$stmt->addParameter(8, $clientSecret);
|
$stmt->addParameter(8, $clientId);
|
||||||
|
$stmt->addParameter(9, $clientSecret);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|
||||||
return $this->getScopeInfo(appId: (string)$this->dbConn->getLastInsertId());
|
return $this->getScopeInfo(appId: (string)$this->dbConn->getLastInsertId());
|
||||||
|
|
|
@ -61,12 +61,25 @@ class OAuth2Context {
|
||||||
$logAction(' Removed %d!', $pruned);
|
$logAction(' Removed %d!', $pruned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createAccess(
|
||||||
|
AppInfo $appInfo,
|
||||||
|
string $scope = '',
|
||||||
|
?string $userInfo = null
|
||||||
|
): OAuth2AccessInfo {
|
||||||
|
return $this->tokens->createAccess(
|
||||||
|
$appInfo,
|
||||||
|
userId: $userInfo,
|
||||||
|
scope: $scope,
|
||||||
|
lifetime: $appInfo->getAccessTokenLifetime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function createRefresh(
|
public function createRefresh(
|
||||||
AppInfo $appInfo,
|
AppInfo $appInfo,
|
||||||
OAuth2AccessInfo $accessInfo
|
OAuth2AccessInfo $accessInfo
|
||||||
): OAuth2RefreshInfo {
|
): OAuth2RefreshInfo {
|
||||||
return $this->tokens->createRefresh(
|
return $this->tokens->createRefresh(
|
||||||
$accessInfo->getAppId(),
|
$appInfo,
|
||||||
$accessInfo,
|
$accessInfo,
|
||||||
userId: $accessInfo->getUserId(),
|
userId: $accessInfo->getUserId(),
|
||||||
scope: $accessInfo->getScope(),
|
scope: $accessInfo->getScope(),
|
||||||
|
@ -172,11 +185,10 @@ class OAuth2Context {
|
||||||
|
|
||||||
$scope = $this->checkAndBuildScopeString($appInfo, $authsInfo->getScope(), true);
|
$scope = $this->checkAndBuildScopeString($appInfo, $authsInfo->getScope(), true);
|
||||||
|
|
||||||
$tokensData = $this->getTokensData();
|
$accessInfo = $this->createAccess(
|
||||||
$accessInfo = $tokensData->createAccess(
|
|
||||||
$appInfo,
|
$appInfo,
|
||||||
|
$scope,
|
||||||
$authsInfo->getUserId(),
|
$authsInfo->getUserId(),
|
||||||
scope: $scope,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if($authsInfo->getScope() === $scope)
|
if($authsInfo->getScope() === $scope)
|
||||||
|
@ -226,10 +238,10 @@ class OAuth2Context {
|
||||||
|
|
||||||
$scope = $this->checkAndBuildScopeString($appInfo, $scope, true);
|
$scope = $this->checkAndBuildScopeString($appInfo, $scope, true);
|
||||||
|
|
||||||
$accessInfo = $tokensData->createAccess(
|
$accessInfo = $this->createAccess(
|
||||||
$appInfo,
|
$appInfo,
|
||||||
|
$scope,
|
||||||
$refreshInfo->getUserId(),
|
$refreshInfo->getUserId(),
|
||||||
scope: $scope,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if($refreshInfo->getScope() === $scope)
|
if($refreshInfo->getScope() === $scope)
|
||||||
|
@ -256,7 +268,7 @@ class OAuth2Context {
|
||||||
|
|
||||||
$requestedScope = $scope;
|
$requestedScope = $scope;
|
||||||
$scope = $this->checkAndBuildScopeString($appInfo, $requestedScope, true);
|
$scope = $this->checkAndBuildScopeString($appInfo, $requestedScope, true);
|
||||||
$accessInfo = $this->getTokensData()->createAccess($appInfo, scope: $scope);
|
$accessInfo = $this->createAccess($appInfo, $scope);
|
||||||
|
|
||||||
if($requestedScope !== null && $scope === '')
|
if($requestedScope !== null && $scope === '')
|
||||||
return [
|
return [
|
||||||
|
@ -323,10 +335,10 @@ class OAuth2Context {
|
||||||
$scope = $this->checkAndBuildScopeString($appInfo, $deviceInfo->getScope(), true);
|
$scope = $this->checkAndBuildScopeString($appInfo, $deviceInfo->getScope(), true);
|
||||||
|
|
||||||
$tokensData = $this->getTokensData();
|
$tokensData = $this->getTokensData();
|
||||||
$accessInfo = $tokensData->createAccess(
|
$accessInfo = $this->createAccess(
|
||||||
$appInfo,
|
$appInfo,
|
||||||
|
$scope,
|
||||||
$deviceInfo->getUserId(),
|
$deviceInfo->getUserId(),
|
||||||
scope: $scope,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// 'scope' only has to be in the response if it differs from what was requested
|
// 'scope' only has to be in the response if it differs from what was requested
|
||||||
|
|
Loading…
Reference in a new issue