Resolved merge conflict
This commit is contained in:
commit
c2c6d337ed
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -2336,7 +2336,8 @@ App::post('/v1/account/tokens/phone')
|
||||||
;
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/account/jwt')
|
App::post('/v1/account/jwts')
|
||||||
|
->alias('/v1/account/jwt')
|
||||||
->desc('Create JWT')
|
->desc('Create JWT')
|
||||||
->groups(['api', 'account', 'auth'])
|
->groups(['api', 'account', 'auth'])
|
||||||
->label('scope', 'account')
|
->label('scope', 'account')
|
||||||
|
@ -2369,15 +2370,11 @@ App::post('/v1/account/jwt')
|
||||||
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
|
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 0);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
->dynamic(new Document(['jwt' => $jwt->encode([
|
->dynamic(new Document(['jwt' => $jwt->encode([
|
||||||
// 'uid' => 1,
|
|
||||||
// 'aud' => 'http://site.com',
|
|
||||||
// 'scopes' => ['user'],
|
|
||||||
// 'iss' => 'http://api.mysite.com',
|
|
||||||
'userId' => $user->getId(),
|
'userId' => $user->getId(),
|
||||||
'sessionId' => $current->getId(),
|
'sessionId' => $current->getId(),
|
||||||
])]), Response::MODEL_JWT);
|
])]), Response::MODEL_JWT);
|
||||||
|
|
|
@ -1433,9 +1433,10 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
|
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Create build')
|
->desc('Rebuild deployment')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
|
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
|
||||||
->label('audits.event', 'deployment.update')
|
->label('audits.event', 'deployment.update')
|
||||||
|
@ -1447,7 +1448,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||||
->label('sdk.response.model', Response::MODEL_NONE)
|
->label('sdk.response.model', Response::MODEL_NONE)
|
||||||
->param('functionId', '', new UID(), 'Function ID.')
|
->param('functionId', '', new UID(), 'Function ID.')
|
||||||
->param('deploymentId', '', new UID(), 'Deployment ID.')
|
->param('deploymentId', '', new UID(), 'Deployment ID.')
|
||||||
->param('buildId', '', new UID(), 'Build unique ID.')
|
->param('buildId', '', new UID(), 'Build unique ID.', true) // added as optional param for backward compatibility
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
@ -1455,7 +1456,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForBuilds')
|
->inject('queueForBuilds')
|
||||||
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds) {
|
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds) {
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
|
@ -1466,11 +1466,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||||
if ($deployment->isEmpty()) {
|
if ($deployment->isEmpty()) {
|
||||||
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
|
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId));
|
|
||||||
|
|
||||||
if ($build->isEmpty()) {
|
|
||||||
throw new Exception(Exception::BUILD_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$deploymentId = ID::unique();
|
$deploymentId = ID::unique();
|
||||||
|
|
||||||
|
@ -1654,7 +1649,8 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$current->isEmpty()) {
|
if (!$current->isEmpty()) {
|
||||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
|
$jwtExpiry = $function->getAttribute('timeout', 900);
|
||||||
|
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||||
$jwt = $jwtObj->encode([
|
$jwt = $jwtObj->encode([
|
||||||
'userId' => $user->getId(),
|
'userId' => $user->getId(),
|
||||||
'sessionId' => $current->getId(),
|
'sessionId' => $current->getId(),
|
||||||
|
@ -1663,7 +1659,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
}
|
}
|
||||||
|
|
||||||
$jwtExpiry = $function->getAttribute('timeout', 900);
|
$jwtExpiry = $function->getAttribute('timeout', 900);
|
||||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 10);
|
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||||
$apiKey = $jwtObj->encode([
|
$apiKey = $jwtObj->encode([
|
||||||
'projectId' => $project->getId(),
|
'projectId' => $project->getId(),
|
||||||
'scopes' => $function->getAttribute('scopes', [])
|
'scopes' => $function->getAttribute('scopes', [])
|
||||||
|
|
|
@ -2939,11 +2939,9 @@ App::post('/v1/messaging/messages/push')
|
||||||
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
|
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
|
||||||
}
|
}
|
||||||
|
|
||||||
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
|
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', \intval($expiry), 0);
|
||||||
|
|
||||||
$jwt = $encoder->encode([
|
$jwt = $encoder->encode([
|
||||||
'iat' => \time(),
|
|
||||||
'exp' => $expiry,
|
|
||||||
'bucketId' => $bucket->getId(),
|
'bucketId' => $bucket->getId(),
|
||||||
'fileId' => $file->getId(),
|
'fileId' => $file->getId(),
|
||||||
'projectId' => $project->getId(),
|
'projectId' => $project->getId(),
|
||||||
|
@ -3801,11 +3799,9 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||||
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
|
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
|
||||||
}
|
}
|
||||||
|
|
||||||
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
|
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', \intval($expiry), 0);
|
||||||
|
|
||||||
$jwt = $encoder->encode([
|
$jwt = $encoder->encode([
|
||||||
'iat' => \time(),
|
|
||||||
'exp' => $expiry,
|
|
||||||
'bucketId' => $bucket->getId(),
|
'bucketId' => $bucket->getId(),
|
||||||
'fileId' => $file->getId(),
|
'fileId' => $file->getId(),
|
||||||
'projectId' => $project->getId(),
|
'projectId' => $project->getId(),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Ahc\Jwt\JWT;
|
||||||
use Appwrite\Auth\Auth;
|
use Appwrite\Auth\Auth;
|
||||||
use Appwrite\Event\Delete;
|
use Appwrite\Event\Delete;
|
||||||
use Appwrite\Event\Mail;
|
use Appwrite\Event\Mail;
|
||||||
|
@ -1309,6 +1310,41 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// JWT Keys
|
||||||
|
|
||||||
|
App::post('/v1/projects/:projectId/jwts')
|
||||||
|
->groups(['api', 'projects'])
|
||||||
|
->desc('Create JWT')
|
||||||
|
->label('scope', 'projects.write')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'projects')
|
||||||
|
->label('sdk.method', 'createJWT')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_JWT)
|
||||||
|
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||||
|
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||||
|
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForConsole')
|
||||||
|
->action(function (string $projectId, array $scopes, int $duration, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
|
if ($project->isEmpty()) {
|
||||||
|
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
|
||||||
|
|
||||||
|
$response
|
||||||
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
|
->dynamic(new Document(['jwt' => API_KEY_DYNAMIC . '_' . $jwt->encode([
|
||||||
|
'projectId' => $project->getId(),
|
||||||
|
'scopes' => $scopes
|
||||||
|
])]), Response::MODEL_JWT);
|
||||||
|
});
|
||||||
|
|
||||||
// Platforms
|
// Platforms
|
||||||
|
|
||||||
App::post('/v1/projects/:projectId/platforms')
|
App::post('/v1/projects/:projectId/platforms')
|
||||||
|
|
|
@ -1328,7 +1328,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||||
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) {
|
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) {
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
|
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$decoded = $decoder->decode($jwt);
|
$decoded = $decoder->decode($jwt);
|
||||||
|
@ -1339,8 +1339,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||||
if (
|
if (
|
||||||
$decoded['projectId'] !== $project->getId() ||
|
$decoded['projectId'] !== $project->getId() ||
|
||||||
$decoded['bucketId'] !== $bucketId ||
|
$decoded['bucketId'] !== $bucketId ||
|
||||||
$decoded['fileId'] !== $fileId ||
|
$decoded['fileId'] !== $fileId
|
||||||
$decoded['exp'] < \time()
|
|
||||||
) {
|
) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Ahc\Jwt\JWT;
|
||||||
use Appwrite\Auth\Auth;
|
use Appwrite\Auth\Auth;
|
||||||
use Appwrite\Auth\MFA\Type;
|
use Appwrite\Auth\MFA\Type;
|
||||||
use Appwrite\Auth\MFA\Type\TOTP;
|
use Appwrite\Auth\MFA\Type\TOTP;
|
||||||
|
@ -39,6 +40,7 @@ use Utopia\Database\Validator\Query\Limit;
|
||||||
use Utopia\Database\Validator\Query\Offset;
|
use Utopia\Database\Validator\Query\Offset;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
use Utopia\Validator\ArrayList;
|
||||||
use Utopia\Validator\Assoc;
|
use Utopia\Validator\Assoc;
|
||||||
use Utopia\Validator\Boolean;
|
use Utopia\Validator\Boolean;
|
||||||
|
@ -2091,6 +2093,60 @@ App::delete('/v1/users/identities/:identityId')
|
||||||
return $response->noContent();
|
return $response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::post('/v1/users/:userId/jwts')
|
||||||
|
->desc('Create user JWT')
|
||||||
|
->groups(['api', 'users'])
|
||||||
|
->label('scope', 'users.write')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->label('sdk.method', 'createJWT')
|
||||||
|
->label('sdk.description', '/docs/references/users/create-user-jwt.md')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_JWT)
|
||||||
|
->param('userId', '', new UID(), 'User ID.')
|
||||||
|
->param('sessionId', 'recent', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
|
||||||
|
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $userId, string $sessionId, int $duration, Response $response, Database $dbForProject) {
|
||||||
|
|
||||||
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
if ($user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
$session = new Document();
|
||||||
|
|
||||||
|
if($sessionId === 'recent') {
|
||||||
|
// Get most recent
|
||||||
|
$session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document();
|
||||||
|
} else {
|
||||||
|
// Find by ID
|
||||||
|
foreach ($sessions as $loopSession) { /** @var Utopia\Database\Document $loopSession */
|
||||||
|
if ($loopSession->getId() == $sessionId) {
|
||||||
|
$session = $loopSession;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($session->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
|
||||||
|
|
||||||
|
$response
|
||||||
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
|
->dynamic(new Document(['jwt' => $jwt->encode([
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'sessionId' => $session->getId()
|
||||||
|
])]), Response::MODEL_JWT);
|
||||||
|
});
|
||||||
|
|
||||||
App::get('/v1/users/usage')
|
App::get('/v1/users/usage')
|
||||||
->desc('Get users usage stats')
|
->desc('Get users usage stats')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
|
|
|
@ -216,7 +216,7 @@ App::init()
|
||||||
if($keyType === API_KEY_DYNAMIC) {
|
if($keyType === API_KEY_DYNAMIC) {
|
||||||
// Dynamic key
|
// Dynamic key
|
||||||
|
|
||||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10);
|
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$payload = $jwtObj->decode($authKey);
|
$payload = $jwtObj->decode($authKey);
|
||||||
|
|
|
@ -1233,7 +1233,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||||
$authJWT = $request->getHeader('x-appwrite-jwt', '');
|
$authJWT = $request->getHeader('x-appwrite-jwt', '');
|
||||||
|
|
||||||
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
|
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
|
||||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
|
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$payload = $jwt->decode($authJWT);
|
$payload = $jwt->decode($authJWT);
|
||||||
|
|
1
docs/references/users/create-user-jwt.md
Normal file
1
docs/references/users/create-user-jwt.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Use this endpoint to create a JSON Web Token for user by its unique ID. You can use the resulting JWT to authenticate on behalf of the user. The JWT secret will become invalid if the session it uses gets deleted.
|
|
@ -284,7 +284,7 @@ class Functions extends Action
|
||||||
$runtime = $runtimes[$function->getAttribute('runtime')];
|
$runtime = $runtimes[$function->getAttribute('runtime')];
|
||||||
|
|
||||||
$jwtExpiry = $function->getAttribute('timeout', 900);
|
$jwtExpiry = $function->getAttribute('timeout', 900);
|
||||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 10);
|
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||||
$apiKey = $jwtObj->encode([
|
$apiKey = $jwtObj->encode([
|
||||||
'projectId' => $project->getId(),
|
'projectId' => $project->getId(),
|
||||||
'scopes' => $function->getAttribute('scopes', [])
|
'scopes' => $function->getAttribute('scopes', [])
|
||||||
|
|
|
@ -2743,6 +2743,60 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
$this->assertEmpty($response['body']);
|
$this->assertEmpty($response['body']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JWT Keys
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateProject
|
||||||
|
*/
|
||||||
|
public function testJWTKey($data): void
|
||||||
|
{
|
||||||
|
$id = $data['projectId'] ?? '';
|
||||||
|
|
||||||
|
// Create JWT key
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/jwts', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'duration' => 5,
|
||||||
|
'scopes' => ['users.read'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
$this->assertNotEmpty($response['body']['jwt']);
|
||||||
|
|
||||||
|
$jwt = $response['body']['jwt'];
|
||||||
|
|
||||||
|
// Ensure JWT key works
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/users', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
'x-appwrite-key' => $jwt,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertArrayHasKey('users', $response['body']);
|
||||||
|
|
||||||
|
// Ensure JWT key respect scopes
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/functions', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
'x-appwrite-key' => $jwt,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
// Ensure JWT key expires
|
||||||
|
\sleep(10);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/users', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
'x-appwrite-key' => $jwt,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response['headers']['status-code']);
|
||||||
|
}
|
||||||
|
|
||||||
// Platforms
|
// Platforms
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1553,6 +1553,137 @@ trait UsersBase
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUserJWT()
|
||||||
|
{
|
||||||
|
// Create user
|
||||||
|
$userId = ID::unique();
|
||||||
|
$user = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'userId' => $userId,
|
||||||
|
'email' => 'jwtuser@appwrite.io',
|
||||||
|
'password' => 'password',
|
||||||
|
], false);
|
||||||
|
$this->assertEquals($user['headers']['status-code'], 201);
|
||||||
|
|
||||||
|
// Create two sessions
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
]), [
|
||||||
|
'email' => 'jwtuser@appwrite.io',
|
||||||
|
'password' => 'password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals($userId, $response['body']['userId']);
|
||||||
|
$this->assertNotEmpty($response['body']['$id']);
|
||||||
|
$session1Id = $response['body']['$id'];
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
]), [
|
||||||
|
'email' => 'jwtuser@appwrite.io',
|
||||||
|
'password' => 'password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals($userId, $response['body']['userId']);
|
||||||
|
$this->assertNotEmpty($response['body']['$id']);
|
||||||
|
$session2Id = $response['body']['$id'];
|
||||||
|
|
||||||
|
// Create JWT 1 for older session by ID
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'sessionId' => $session1Id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
$this->assertNotEmpty($response['body']['jwt']);
|
||||||
|
$jwt1 = $response['body']['jwt'];
|
||||||
|
|
||||||
|
// Ensure JWT 1 works
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-jwt' => $jwt1,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals($userId, $response['body']['$id']);
|
||||||
|
|
||||||
|
// Create JWT 2 for latest session using default param
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'duration' => 5
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
$this->assertNotEmpty($response['body']['jwt']);
|
||||||
|
$jwt2 = $response['body']['jwt'];
|
||||||
|
|
||||||
|
// Ensure JWT 2 works
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-jwt' => $jwt2,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals($userId, $response['body']['$id']);
|
||||||
|
|
||||||
|
// Wait, ensure JWT 2 no longer works because of short duration
|
||||||
|
|
||||||
|
\sleep(10);
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-jwt' => $jwt2,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
// Delete session, ensure JWT 1 no longer works because of session missing
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId . '/sessions', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'sessionId' => $session1Id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(204, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-jwt' => $jwt1,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
// Cleanup after test
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 204);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO add test for session delete
|
// TODO add test for session delete
|
||||||
// TODO add test for all sessions delete
|
// TODO add test for all sessions delete
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue