Compare commits

...
Author SHA1 Message Date
Chirag Aggarwal fc5fc6de58 add docs 2025-10-19 12:28:34 +05:30
Chirag Aggarwal 8bba7880d8 queue for events 2025-10-19 12:26:46 +05:30
Chirag Aggarwal e8a9122421 fix: constructor 2025-10-19 12:25:28 +05:30
Chirag Aggarwal 75a5d19ae0 remove USER_TOKEN_ALREADY_EXISTS 2025-10-19 12:24:47 +05:30
Chirag Aggarwal 03b8594bc6 remove publicKey for now 2025-10-19 12:23:49 +05:30
Chirag Aggarwal e73aa26055 feat: browser based cli login 2025-10-19 01:09:40 +05:30
5 changed files with 94 additions and 0 deletions
+7
View File
@@ -52,4 +52,11 @@ return [
'docs' => 'https://appwrite.io/docs/references/cloud/client-web/account#accountCreatePhoneToken',
'enabled' => true,
],
'cli' => [
'name' => 'CLI',
'key' => 'cli',
'icon' => '/images/users/cli.png',
'docs' => 'https://appwrite.io/docs/references/cloud/client-web/account#accountCreateCLIToken',
'enabled' => true,
],
];
+75
View File
@@ -200,6 +200,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$factor = (match ($verifiedToken->getAttribute('type')) {
TOKEN_TYPE_MAGIC_URL,
TOKEN_TYPE_OAUTH2,
TOKEN_TYPE_CLI,
TOKEN_TYPE_EMAIL => Type::EMAIL,
TOKEN_TYPE_PHONE => Type::PHONE,
TOKEN_TYPE_GENERIC => 'token',
@@ -2784,6 +2785,80 @@ App::post('/v1/account/tokens/phone')
->dynamic($token, Response::MODEL_TOKEN);
});
App::post('/v1/account/tokens/cli')
->desc('Create CLI token')
->groups(['api', 'account', 'auth'])
->label('scope', 'sessions.write')
->label('auth.type', 'cli')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createCLIToken',
description: '/docs/references/account/create-token-cli.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON,
))
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},ip:{ip}')
->inject('request')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('proofForCode')
->inject('queueForEvents')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, ProofsCode $proofForCode, Event $queueForEvents) {
if ($user->isEmpty()) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$secret = strtoupper(substr($proofForCode->generate(), 0, 6));
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CLI));
$token = new Document([
'$id' => ID::unique(),
'userId' => $user->getId(),
'userInternalId' => $user->getSequence(),
'type' => TOKEN_TYPE_CLI,
'secret' => $proofForCode->hash($secret),
'expire' => $expire,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->purgeCachedDocument('users', $user->getId());
$token->setAttribute('secret', $secret); // return secret in plain text to the client
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json([
'$id' => $token->getId(),
'$createdAt' => DateTime::formatTz($token->getAttribute('$createdAt')),
'userId' => $token->getAttribute('userId'),
'secret' => $secret,
'expire' => DateTime::formatTz($token->getAttribute('expire')),
]);
});
App::post('/v1/account/jwts')
->alias('/v1/account/jwt')
->desc('Create JWT')
+6
View File
@@ -100,6 +100,12 @@ App::init()
}
break;
case 'cli':
if (($auths[Config::getParam('auth')['cli']['key']] ?? true) === false) {
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'CLI authentication is disabled for this project');
}
break;
default:
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route');
}
+3
View File
@@ -111,6 +111,7 @@ const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
const TOKEN_EXPIRATION_CLI = 60 * 15; /* 15 minutes */
/**
* Token Lengths.
@@ -133,6 +134,7 @@ const TOKEN_TYPE_PHONE = 6;
const TOKEN_TYPE_OAUTH2 = 7;
const TOKEN_TYPE_GENERIC = 8;
const TOKEN_TYPE_EMAIL = 9; // OTP
const TOKEN_TYPE_CLI = 10;
/**
* Session Providers.
@@ -144,6 +146,7 @@ const SESSION_PROVIDER_PHONE = 'phone';
const SESSION_PROVIDER_OAUTH2 = 'oauth2';
const SESSION_PROVIDER_TOKEN = 'token';
const SESSION_PROVIDER_SERVER = 'server';
const SESSION_PROVIDER_CLI = 'cli';
/**
* Activity associated with user or the app.
@@ -0,0 +1,3 @@
Initiates a browser-based authentication flow for CLI applications. This endpoint generates a token that allows users to authenticate from their browser and then return to the CLI with valid credentials. The user will be redirected to their browser where they can complete the authentication process. Once authenticated, the secret and userId will be made available to the CLI application. Use the returned user ID and secret and submit a request to the [POST /v1/account/sessions/token](https://appwrite.io/docs/references/cloud/client-web/account#createSession) endpoint to complete the login process. The token is valid for 15 minutes.
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](https://appwrite.io/docs/authentication-security#limits).