1
0
Fork 0
mirror of synced 2024-05-20 20:52:36 +12:00

Updated naming from OAuth to OAuth2

This commit is contained in:
Eldad Fux 2020-02-16 13:41:03 +02:00
parent 7aff7a40ad
commit f433db17d0
66 changed files with 250 additions and 1058 deletions

View file

@ -169,5 +169,6 @@ docker exec appwrite /bin/bash -c '/usr/share/nginx/html/vendor/bin/phpunit'
From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials:
* [Adding Support for a New OAuth Provider](./docs/tutorials/add-oauth-provider.md)
* [Appwrite Environment Variables](./docs/tutorials/add-oauth-provider.md)
* [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
* [Appwrite Environment Variables](./docs/tutorials/environment-variables.md)
* [Running in Production](./docs/tutorials/running-in-production.md)

View file

@ -1078,7 +1078,7 @@ $collections = [
];
/*
* Add enabled OAuth providers to default data rules
* Add enabled OAuth2 providers to default data rules
*/
foreach ($providers as $key => $provider) {
if (!$provider['enabled']) {
@ -1087,8 +1087,8 @@ foreach ($providers as $key => $provider) {
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth '.ucfirst($key).' ID',
'key' => 'usersOauth'.ucfirst($key).'Appid',
'label' => 'OAuth2 '.ucfirst($key).' ID',
'key' => 'usersOauth2'.ucfirst($key).'Appid',
'type' => 'text',
'default' => '',
'required' => false,
@ -1097,8 +1097,8 @@ foreach ($providers as $key => $provider) {
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth '.ucfirst($key).' Secret',
'key' => 'usersOauth'.ucfirst($key).'Secret',
'label' => 'OAuth2 '.ucfirst($key).' Secret',
'key' => 'usersOauth2'.ucfirst($key).'Secret',
'type' => 'text',
'default' => '',
'required' => false,
@ -1107,8 +1107,8 @@ foreach ($providers as $key => $provider) {
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth '.ucfirst($key).' ID',
'key' => 'oauth'.ucfirst($key),
'label' => 'OAuth2 '.ucfirst($key).' ID',
'key' => 'oauth2'.ucfirst($key),
'type' => 'text',
'default' => '',
'required' => false,
@ -1117,8 +1117,8 @@ foreach ($providers as $key => $provider) {
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth '.ucfirst($key).' Access Token',
'key' => 'oauth'.ucfirst($key).'AccessToken',
'label' => 'OAuth2 '.ucfirst($key).' Access Token',
'key' => 'oauth2'.ucfirst($key).'AccessToken',
'type' => 'text',
'default' => '',
'required' => false,

View file

@ -27,16 +27,16 @@ use OpenSSL\OpenSSL;
include_once __DIR__ . '/../shared/api.php';
$oauthKeys = [];
$oauth2Keys = [];
$utopia->init(function() use ($providers, &$oauthKeys) {
$utopia->init(function() use ($providers, &$oauth2Keys) {
foreach ($providers as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauthKeys[] = 'oauth'.ucfirst($key);
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
$oauth2Keys[] = 'oauth2'.ucfirst($key);
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
}
});
@ -54,7 +54,7 @@ $utopia->post('/v1/account')
->param('password', '', function () { return new Password(); }, 'User password.')
->param('name', '', function () { return new Text(100); }, 'User name.', true)
->action(
function ($email, $password, $name) use ($register, $request, $response, $audit, $projectDB, $project, $webhook, $oauthKeys) {
function ($email, $password, $name) use ($register, $request, $response, $audit, $projectDB, $project, $webhook, $oauth2Keys) {
if ('console' === $project->getUid()) {
$whitlistEmails = $project->getAttribute('authWhitelistEmails');
$whitlistIPs = $project->getAttribute('authWhitelistIPs');
@ -132,7 +132,7 @@ $utopia->post('/v1/account')
'registration',
'name',
],
$oauthKeys
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
}
);
@ -220,27 +220,27 @@ $utopia->post('/v1/account/sessions')
}
);
$utopia->get('/v1/account/sessions/oauth/:provider')
->desc('Create Account Session with OAuth')
$utopia->get('/v1/account/sessions/oauth2/:provider')
->desc('Create Account Session with OAuth2')
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createOAuthSession')
->label('sdk.description', '/docs/references/account/create-session-oauth.md')
->label('sdk.method', 'createOAuth2Session')
->label('sdk.description', '/docs/references/account/create-session-oauth2.md')
->label('sdk.response.code', 301)
->label('sdk.response.type', 'text/html')
->label('sdk.location', true)
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth Provider. Currently, supported providers are: ' . implode(', ', array_keys(array_filter($providers, function($node) {return (!$node['mock']);}))).'.')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth2 Provider. Currently, supported providers are: ' . implode(', ', array_keys(array_filter($providers, function($node) {return (!$node['mock']);}))).'.')
->param('success', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt.')
->param('failure', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt.')
->action(
function ($provider, $success, $failure) use ($response, $request, $project) {
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth/callback/'.$provider.'/'.$project->getUid();
$appId = $project->getAttribute('usersOauth'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}');
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getUid();
$appId = $project->getAttribute('usersOauth2'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}');
$appSecret = json_decode($appSecret, true);
@ -253,53 +253,53 @@ $utopia->get('/v1/account/sessions/oauth/:provider')
throw new Exception('Provider is undefined, configure provider app ID and app secret key to continue', 412);
}
$classname = 'Auth\\OAuth\\'.ucfirst($provider);
$classname = 'Auth\\OAuth2\\'.ucfirst($provider);
if (!class_exists($classname)) {
throw new Exception('Provider is not supported', 501);
}
$oauth = new $classname($appId, $appSecret, $callback, ['success' => $success, 'failure' => $failure]);
$oauth2 = new $classname($appId, $appSecret, $callback, ['success' => $success, 'failure' => $failure]);
$response->redirect($oauth->getLoginURL());
$response->redirect($oauth2->getLoginURL());
}
);
$utopia->get('/v1/account/sessions/oauth/callback/:provider/:projectId')
->desc('OAuth Callback')
$utopia->get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 Callback')
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public')
->label('docs', false)
->param('projectId', '', function () { return new Text(1024); }, 'Project unique ID.')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth code.')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
->action(
function ($projectId, $provider, $code, $state) use ($response, $request, $domain) {
$response->redirect($request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/v1/account/sessions/oauth/'.$provider.'/redirect?'
$response->redirect($request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
.http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}
);
$utopia->get('/v1/account/sessions/oauth/:provider/redirect')
->desc('OAuth Redirect')
$utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 Redirect')
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('webhook', 'account.sessions.create')
->label('scope', 'public')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->label('docs', false)
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth code.')
->param('state', '', function () { return new Text(2048); }, 'OAuth state params.', true)
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'OAuth2 state params.', true)
->action(
function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit) {
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth/callback/'.$provider.'/'.$project->getUid();
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getUid();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
$appId = $project->getAttribute('usersOauth'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}');
$appId = $project->getAttribute('usersOauth2'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}');
$appSecret = json_decode($appSecret, true);
@ -308,19 +308,19 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
$appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
}
$classname = 'Auth\\OAuth\\'.ucfirst($provider);
$classname = 'Auth\\OAuth2\\'.ucfirst($provider);
if (!class_exists($classname)) {
throw new Exception('Provider is not supported', 501);
}
$oauth = new $classname($appId, $appSecret, $callback);
$oauth2 = new $classname($appId, $appSecret, $callback);
if (!empty($state)) {
try {
$state = array_merge($defaultState, $oauth->parseState($state));
$state = array_merge($defaultState, $oauth2->parseState($state));
} catch (\Exception $exception) {
throw new Exception('Failed to parse login state params as passed from OAuth provider');
throw new Exception('Failed to parse login state params as passed from OAuth2 provider');
}
} else {
$state = $defaultState;
@ -334,7 +334,7 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
throw new Exception('Invalid redirect URL for failure login', 400);
}
$state['failure'] = null;
$accessToken = $oauth->getAccessToken($code);
$accessToken = $oauth2->getAccessToken($code);
if (empty($accessToken)) {
if (!empty($state['failure'])) {
@ -344,14 +344,14 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
throw new Exception('Failed to obtain access token');
}
$oauthID = $oauth->getUserID($accessToken);
$oauth2ID = $oauth2->getUserID($accessToken);
if (empty($oauthID)) {
if (empty($oauth2ID)) {
if (!empty($state['failure'])) {
$response->redirect($state['failure'], 301, 0);
}
throw new Exception('Missing ID from OAuth provider', 400);
throw new Exception('Missing ID from OAuth2 provider', 400);
}
$current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
@ -365,13 +365,13 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'oauth'.ucfirst($provider).'='.$oauthID,
'oauth2'.ucfirst($provider).'='.$oauth2ID,
],
]) : $user;
if (empty($user)) { // No user logged in or with oauth provider ID, create new one or connect with account with same email
$name = $oauth->getUserName($accessToken);
$email = $oauth->getUserEmail($accessToken);
if (empty($user)) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
$name = $oauth2->getUserName($accessToken);
$email = $oauth2->getUserEmail($accessToken);
$user = $projectDB->getCollection([ // Get user by provider email address
'limit' => 1,
@ -390,7 +390,7 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
'$permissions' => ['read' => ['*'], 'write' => ['user:{self}']],
'email' => $email,
'emailVerification' => true,
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth provider
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider
'password' => Auth::passwordHash(Auth::passwordGenerator()),
'password-update' => time(),
'registration' => time(),
@ -406,7 +406,7 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
}
}
// Create session token, verify user account and update OAuth ID and Access Token
// Create session token, verify user account and update OAuth2 ID and Access Token
$secret = Auth::tokenGenerator();
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
@ -421,8 +421,8 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect')
]);
$user
->setAttribute('oauth'.ucfirst($provider), $oauthID)
->setAttribute('oauth'.ucfirst($provider).'AccessToken', $accessToken)
->setAttribute('oauth2'.ucfirst($provider), $oauth2ID)
->setAttribute('oauth2'.ucfirst($provider).'AccessToken', $accessToken)
->setAttribute('status', Auth::USER_STATUS_ACTIVATED)
->setAttribute('tokens', $session, Document::SET_TYPE_APPEND)
;
@ -459,7 +459,7 @@ $utopia->get('/v1/account')
->label('sdk.description', '/docs/references/account/get.md')
->label('sdk.response', ['200' => 'user'])
->action(
function () use ($response, &$user, $oauthKeys) {
function () use ($response, &$user, $oauth2Keys) {
$response->json(array_merge($user->getArrayCopy(array_merge(
[
'$uid',
@ -467,7 +467,7 @@ $utopia->get('/v1/account')
'registration',
'name',
],
$oauthKeys
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
}
);
@ -634,7 +634,7 @@ $utopia->patch('/v1/account/name')
->label('sdk.description', '/docs/references/account/update-name.md')
->param('name', '', function () { return new Text(100); }, 'User name.')
->action(
function ($name) use ($response, $user, $projectDB, $audit, $oauthKeys) {
function ($name) use ($response, $user, $projectDB, $audit, $oauth2Keys) {
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
'name' => $name,
]));
@ -656,7 +656,7 @@ $utopia->patch('/v1/account/name')
'registration',
'name',
],
$oauthKeys
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
}
);
@ -672,7 +672,7 @@ $utopia->patch('/v1/account/password')
->param('password', '', function () { return new Password(); }, 'New user password.')
->param('old-password', '', function () { return new Password(); }, 'Old user password.')
->action(
function ($password, $oldPassword) use ($response, $user, $projectDB, $audit, $oauthKeys) {
function ($password, $oldPassword) use ($response, $user, $projectDB, $audit, $oauth2Keys) {
if (!Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password
throw new Exception('Invalid credentials', 401);
}
@ -698,7 +698,7 @@ $utopia->patch('/v1/account/password')
'registration',
'name',
],
$oauthKeys
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
}
);
@ -714,7 +714,7 @@ $utopia->patch('/v1/account/email')
->param('email', '', function () { return new Email(); }, 'User email.')
->param('password', '', function () { return new Password(); }, 'User password.')
->action(
function ($email, $password) use ($response, $user, $projectDB, $audit, $oauthKeys) {
function ($email, $password) use ($response, $user, $projectDB, $audit, $oauth2Keys) {
if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password
throw new Exception('Invalid credentials', 401);
}
@ -756,7 +756,7 @@ $utopia->patch('/v1/account/email')
'registration',
'name',
],
$oauthKeys
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
}
);

View file

@ -1,822 +0,0 @@
<?php
global $utopia, $register, $request, $response, $user, $audit, $webhook, $project, $domain, $projectDB, $providers, $clients;
use Utopia\Exception;
use Utopia\Validator\WhiteList;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Text;
use Utopia\Validator\Email;
use Utopia\Validator\Host;
use Utopia\Validator\URL;
use Utopia\Locale\Locale;
use Auth\Auth;
use Auth\Validator\Password;
use Database\Database;
use Database\Document;
use Database\Validator\Authorization;
use Database\Validator\UID;
use Template\Template;
use OpenSSL\OpenSSL;
include_once __DIR__ . '/../shared/api.php';
$utopia->post('/v1/auth/register')
->desc('Register')
->label('webhook', 'auth.register')
->label('scope', 'auth')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'register')
->label('sdk.description', '/docs/references/auth/register.md')
->label('sdk.cookies', true)
->label('abuse-limit', 10)
->param('email', '', function () { return new Email(); }, 'Account email')
->param('password', '', function () { return new Password(); }, 'User password')
->param('confirm', '', function () use ($clients) { return new Host($clients); }, 'Confirmation URL to redirect user after confirm token has been sent to user email') // TODO add our own built-in confirm page
->param('success', null, function () use ($clients) { return new Host($clients); }, 'Redirect when registration succeed', true)
->param('failure', null, function () use ($clients) { return new Host($clients); }, 'Redirect when registration failed', true)
->param('name', '', function () { return new Text(100); }, 'User name', true)
->action(
function ($email, $password, $confirm, $success, $failure, $name) use ($request, $response, $register, $audit, $projectDB, $project, $webhook) {
if ('console' === $project->getUid()) {
$whitlistEmails = $project->getAttribute('authWhitelistEmails');
$whitlistIPs = $project->getAttribute('authWhitelistIPs');
$whitlistDomains = $project->getAttribute('authWhitelistDomains');
if (!empty($whitlistEmails) && !in_array($email, $whitlistEmails)) {
throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401);
}
if (!empty($whitlistIPs) && !in_array($request->getIP(), $whitlistIPs)) {
throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401);
}
if (!empty($whitlistDomains) && !in_array(substr(strrchr($email, '@'), 1), $whitlistDomains)) {
throw new Exception('Console registration is restricted to specific domains. Contact your administrator for more information.', 401);
}
}
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'email='.$email,
],
]);
if (!empty($profile)) {
if ($failure) {
$response->redirect($failure); // .'?message=User already registered'
return;
}
throw new Exception('User already registered', 400);
}
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$confirmSecret = Auth::tokenGenerator();
$loginSecret = Auth::tokenGenerator();
Authorization::disable();
$user = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_USERS,
'$permissions' => [
'read' => ['*'],
'write' => ['user:{self}'],
],
'email' => $email,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
'password-update' => time(),
'registration' => time(),
'confirm' => false,
'reset' => false,
'name' => $name,
]);
Authorization::enable();
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
Authorization::setRole('user:'.$user->getUid());
$user
->setAttribute('tokens', new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
'type' => Auth::TOKEN_TYPE_VERIFICATION,
'secret' => Auth::hash($confirmSecret), // On way hash encryption to protect DB leak
'expire' => time() + Auth::TOKEN_EXPIRATION_CONFIRM,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND)
->setAttribute('tokens', new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
'type' => Auth::TOKEN_TYPE_LOGIN,
'secret' => Auth::hash($loginSecret), // On way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND)
;
$user = $projectDB->createDocument($user->getArrayCopy());
if (false === $user) {
throw new Exception('Failed saving tokens to DB', 500);
}
// Send email address confirmation email
$confirm = Template::parseURL($confirm);
$confirm['query'] = Template::mergeQuery(((isset($confirm['query'])) ? $confirm['query'] : ''), ['userId' => $user->getUid(), 'token' => $confirmSecret]);
$confirm = Template::unParseURL($confirm);
$body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.verification.body'));
$body
->setParam('{{direction}}', Locale::getText('settings.direction'))
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('{{name}}', $name)
->setParam('{{redirect}}', $confirm)
;
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
$mail->addAddress($email, $name);
$mail->Subject = Locale::getText('account.emails.verification.title');
$mail->Body = $body->render();
$mail->AltBody = strip_tags($body->render());
try {
$mail->send();
} catch (\Exception $error) {
// if($failure) {
// $response->redirect($failure);
// return;
// }
// throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
}
$webhook
->setParam('payload', [
'name' => $name,
'email' => $email,
])
;
$audit
->setParam('userId', $user->getUid())
->setParam('event', 'auth.register')
;
$response
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getUid(), $loginSecret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null);
if ($success) {
$response->redirect($success);
}
$response->json(array('result' => 'success'));
}
);
$utopia->post('/v1/auth/register/confirm')
->desc('Confirmation')
->label('webhook', 'auth.confirm')
->label('scope', 'public')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'confirm')
->label('sdk.description', '/docs/references/auth/confirm.md')
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', function () { return new UID(); }, 'User unique ID')
->param('token', '', function () { return new Text(256); }, 'Confirmation secret token')
->action(
function ($userId, $token) use ($response, $request, $projectDB, $audit) {
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'$uid='.$userId,
],
]);
if (empty($profile)) {
throw new Exception('User not found', 404); // TODO maybe hide this
}
$token = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_VERIFICATION, $token);
if (!$token) {
throw new Exception('Confirmation token is not valid', 401);
}
$profile = $projectDB->updateDocument(array_merge($profile->getArrayCopy(), [
'status' => Auth::USER_STATUS_ACTIVATED,
'confirm' => true,
]));
if (false === $profile) {
throw new Exception('Failed saving user to DB', 500);
}
if (!$projectDB->deleteDocument($token)) {
throw new Exception('Failed to remove token from DB', 500);
}
$audit->setParam('event', 'auth.confirm');
$response->json(array('result' => 'success'));
}
);
$utopia->post('/v1/auth/register/confirm/resend')
->desc('Resend Confirmation')
->label('scope', 'account')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'confirmResend')
->label('sdk.description', '/docs/references/auth/confirm-resend.md')
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('confirm', '', function () use ($clients) { return new Host($clients); }, 'Confirmation URL to redirect user to your app after confirm token has been sent to user email.')
->action(
function ($confirm) use ($response, $request, $projectDB, $user, $register, $project) {
if ($user->getAttribute('confirm', false)) {
throw new Exception('Email address is already confirmed', 400);
}
$secret = Auth::tokenGenerator();
$user->setAttribute('tokens', new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
'type' => Auth::TOKEN_TYPE_VERIFICATION,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => time() + Auth::TOKEN_EXPIRATION_CONFIRM,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND);
$user = $projectDB->updateDocument($user->getArrayCopy());
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$confirm = Template::parseURL($confirm);
$confirm['query'] = Template::mergeQuery(((isset($confirm['query'])) ? $confirm['query'] : ''), ['userId' => $user->getUid(), 'token' => $secret]);
$confirm = Template::unParseURL($confirm);
$body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.verification.body'));
$body
->setParam('{{direction}}', Locale::getText('settings.direction'))
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('{{name}}', $user->getAttribute('name'))
->setParam('{{redirect}}', $confirm)
;
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
$mail->addAddress($user->getAttribute('email'), $user->getAttribute('name'));
$mail->Subject = Locale::getText('account.emails.verification.title');
$mail->Body = $body->render();
$mail->AltBody = strip_tags($body->render());
try {
$mail->send();
} catch (\Exception $error) {
//throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
}
$response->json(array('result' => 'success'));
}
);
$utopia->post('/v1/auth/login')
->desc('Login')
->label('webhook', 'auth.login')
->label('scope', 'auth')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'login')
->label('sdk.description', '/docs/references/auth/login.md')
->label('sdk.cookies', true)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', function () { return new Email(); }, 'User account email address')
->param('password', '', function () { return new Password(); }, 'User account password')
->param('success', null, function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt.', true)
->param('failure', null, function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt.', true)
->action(
function ($email, $password, $success, $failure) use ($response, $request, $projectDB, $audit, $webhook) {
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'email='.$email,
],
]);
if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) {
$audit
//->setParam('userId', $profile->getUid())
->setParam('event', 'auth.failure')
;
if ($failure) {
$response->redirect($failure);
return;
}
throw new Exception('Invalid credentials', 401); // Wrong password or username
}
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$profile->setAttribute('tokens', new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$profile->getUid()], 'write' => ['user:'.$profile->getUid()]],
'type' => Auth::TOKEN_TYPE_LOGIN,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND);
Authorization::setRole('user:'.$profile->getUid());
$profile = $projectDB->updateDocument($profile->getArrayCopy());
if (false === $profile) {
throw new Exception('Failed saving user to DB', 500);
}
$webhook
->setParam('payload', [
'name' => $profile->getAttribute('name', ''),
'email' => $profile->getAttribute('email', ''),
])
;
$audit
->setParam('userId', $profile->getUid())
->setParam('event', 'auth.login')
;
$response
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null);
if ($success) {
$response->redirect($success);
}
$response
->json(array('result' => 'success'));
}
);
$utopia->get('/v1/auth/login/oauth/:provider')
->desc('Login with OAuth')
->label('error', __DIR__.'/../views/general/error.phtml')
->label('scope', 'auth')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'oauth')
->label('sdk.description', '/docs/references/auth/login-oauth.md')
->label('sdk.location', true)
->label('sdk.cookies', true)
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth Provider. Currently, supported providers are: ' . implode(', ', array_keys($providers)))
->param('success', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt.')
->param('failure', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt.')
->param('scopes', [], function () { return new ArrayList(new Text(128)); }, 'An array of string where each can be max 128 chars', true)
->action(
function ($provider, $success, $failure, $scopes) use ($response, $request, $project) {
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/auth/login/oauth/callback/'.$provider.'/'.$project->getUid();
$appId = $project->getAttribute('usersOauth'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}');
$appSecret = json_decode($appSecret, true);
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$appSecret['version']);
$appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
}
if (empty($appId) || empty($appSecret)) {
throw new Exception('Provider is undefined, configure provider app ID and app secret key to continue', 412);
}
$classname = 'Auth\\OAuth\\'.ucfirst($provider);
if (!class_exists($classname)) {
throw new Exception('Provider is not supported', 501);
}
$oauth = new $classname($appId, $appSecret, $callback, ['success' => $success, 'failure' => $failure], $scopes);
$response->redirect($oauth->getLoginURL());
}
);
$utopia->get('/v1/auth/login/oauth/callback/:provider/:projectId')
->desc('OAuth Callback')
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'auth')
->label('docs', false)
->param('projectId', '', function () { return new Text(1024); }, 'Project unique ID')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth provider')
->param('code', '', function () { return new Text(1024); }, 'OAuth code')
->param('state', '', function () { return new Text(2048); }, 'Login state params', true)
->action(
function ($projectId, $provider, $code, $state) use ($response, $request, $domain) {
$response->redirect($request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/v1/auth/login/oauth/'.$provider.'/redirect?'
.http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}
);
$utopia->get('/v1/auth/login/oauth/:provider/redirect')
->desc('OAuth Redirect')
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('webhook', 'auth.oauth')
->label('scope', 'auth')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->label('docs', false)
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth provider')
->param('code', '', function () { return new Text(1024); }, 'OAuth code')
->param('state', '', function () { return new Text(2048); }, 'OAuth state params', true)
->action(
function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit) {
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/auth/login/oauth/callback/'.$provider.'/'.$project->getUid();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
$appId = $project->getAttribute('usersOauth'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}');
$appSecret = json_decode($appSecret, true);
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$appSecret['version']);
$appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
}
$classname = 'Auth\\OAuth\\'.ucfirst($provider);
if (!class_exists($classname)) {
throw new Exception('Provider is not supported', 501);
}
$oauth = new $classname($appId, $appSecret, $callback);
if (!empty($state)) {
try {
$state = array_merge($defaultState, $oauth->parseState($state));
} catch (\Exception $exception) {
throw new Exception('Failed to parse login state params as passed from OAuth provider');
}
} else {
$state = $defaultState;
}
if (!$validateURL->isValid($state['success'])) {
throw new Exception('Invalid redirect URL for success login', 400);
}
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
throw new Exception('Invalid redirect URL for failure login', 400);
}
$accessToken = $oauth->getAccessToken($code);
if (empty($accessToken)) {
if (!empty($state['failure'])) {
$response->redirect($state['failure'], 301, 0);
}
throw new Exception('Failed to obtain access token');
}
$oauthID = $oauth->getUserID($accessToken);
if (empty($oauthID)) {
if (!empty($state['failure'])) {
$response->redirect($state['failure'], 301, 0);
}
throw new Exception('Missing ID from OAuth provider', 400);
}
$current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
if ($current) {
$projectDB->deleteDocument($current); //throw new Exception('User already logged in', 401);
}
$user = (empty($user->getUid())) ? $projectDB->getCollection([ // Get user by provider id
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'oauth'.ucfirst($provider).'='.$oauthID,
],
]) : $user;
if (empty($user)) { // No user logged in or with oauth provider ID, create new one or connect with account with same email
$name = $oauth->getUserName($accessToken);
$email = $oauth->getUserEmail($accessToken);
$user = $projectDB->getCollection([ // Get user by provider email address
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'email='.$email,
],
]);
if (!$user || empty($user->getUid())) { // Last option -> create user alone, generate random password
Authorization::disable();
$user = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_USERS,
'$permissions' => ['read' => ['*'], 'write' => ['user:{self}']],
'email' => $email,
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth provider
'password' => Auth::passwordHash(Auth::passwordGenerator()),
'password-update' => time(),
'registration' => time(),
'confirm' => true,
'reset' => false,
'name' => $name,
]);
Authorization::enable();
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
}
}
// Create login token, confirm user account and update OAuth ID and Access Token
$secret = Auth::tokenGenerator();
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$user
->setAttribute('oauth'.ucfirst($provider), $oauthID)
->setAttribute('oauth'.ucfirst($provider).'AccessToken', $accessToken)
->setAttribute('status', Auth::USER_STATUS_ACTIVATED)
->setAttribute('tokens', new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user['$uid']], 'write' => ['user:'.$user['$uid']]],
'type' => Auth::TOKEN_TYPE_LOGIN,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND)
;
Authorization::setRole('user:'.$user->getUid());
$user = $projectDB->updateDocument($user->getArrayCopy());
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$audit
->setParam('userId', $user->getUid())
->setParam('event', 'auth.oauth.login')
->setParam('data', ['provider' => $provider])
;
$response
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
;
$response->redirect($state['success']);
}
);
$utopia->delete('/v1/auth/logout')
->desc('Logout Current Session')
->label('webhook', 'auth.logout')
->label('scope', 'account')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'logout')
->label('sdk.description', '/docs/references/auth/logout.md')
->label('abuse-limit', 100)
->action(
function () use ($response, $request, $user, $projectDB, $audit, $webhook) {
$token = Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
if (!$projectDB->deleteDocument($token)) {
throw new Exception('Failed to remove token from DB', 500);
}
$webhook
->setParam('payload', [
'name' => $user->getAttribute('name', ''),
'email' => $user->getAttribute('email', ''),
])
;
$audit->setParam('event', 'auth.logout');
$response
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->json(array('result' => 'success'))
;
}
);
$utopia->delete('/v1/auth/logout/:id')
->desc('Logout Specific Session')
->label('scope', 'account')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'logoutBySession')
->label('sdk.description', '/docs/references/auth/logout-by-session.md')
->label('abuse-limit', 100)
->param('id', null, function () { return new UID(); }, 'User specific session unique ID number. if 0 delete all sessions.')
->action(
function ($id) use ($response, $request, $user, $projectDB, $audit) {
$tokens = $user->getAttribute('tokens', []);
foreach ($tokens as $token) { /* @var $token Document */
if (($id == $token->getUid() || ($id == 0)) && Auth::TOKEN_TYPE_LOGIN == $token->getAttribute('type')) {
if (!$projectDB->deleteDocument($token->getUid())) {
throw new Exception('Failed to remove token from DB', 500);
}
$audit
->setParam('event', 'auth.logout')
->setParam('resource', '/auth/token/'.$token->getUid())
;
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete cookies
$response->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null);
}
}
}
$response->json(array('result' => 'success'));
}
);
$utopia->post('/v1/auth/recovery')
->desc('Password Recovery')
->label('scope', 'auth')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'recovery')
->label('sdk.description', '/docs/references/auth/recovery.md')
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', function () { return new Email(); }, 'User account email address.')
->param('reset', '', function () use ($clients) { return new Host($clients); }, 'Reset URL in your app to redirect the user after the reset token has been sent to the user email.')
->action(
function ($email, $reset) use ($request, $response, $projectDB, $register, $audit, $project) {
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'email='.$email,
],
]);
if (empty($profile)) {
throw new Exception('User not found', 404); // TODO maybe hide this
}
$secret = Auth::tokenGenerator();
$profile->setAttribute('tokens', new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$profile->getUid()], 'write' => ['user:'.$profile->getUid()]],
'type' => Auth::TOKEN_TYPE_RECOVERY,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'expire' => time() + Auth::TOKEN_EXPIRATION_RECOVERY,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND);
Authorization::setRole('user:'.$profile->getUid());
$profile = $projectDB->updateDocument($profile->getArrayCopy());
if (false === $profile) {
throw new Exception('Failed to save user to DB', 500);
}
$reset = Template::parseURL($reset);
$reset['query'] = Template::mergeQuery(((isset($reset['query'])) ? $reset['query'] : ''), ['userId' => $profile->getUid(), 'token' => $secret]);
$reset = Template::unParseURL($reset);
$body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.recovery.body'));
$body
->setParam('{{direction}}', Locale::getText('settings.direction'))
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('{{name}}', $profile->getAttribute('name'))
->setParam('{{redirect}}', $reset)
;
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
$mail->addAddress($profile->getAttribute('email', ''), $profile->getAttribute('name', ''));
$mail->Subject = Locale::getText('account.emails.recovery.title');
$mail->Body = $body->render();
$mail->AltBody = strip_tags($body->render());
try {
$mail->send();
} catch (\Exception $error) {
//throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
}
$audit
->setParam('userId', $profile->getUid())
->setParam('event', 'auth.recovery')
;
$response->json(array('result' => 'success'));
}
);
$utopia->put('/v1/auth/recovery/reset')
->desc('Password Reset')
->label('scope', 'auth')
->label('sdk.namespace', 'auth')
->label('sdk.method', 'recoveryReset')
->label('sdk.description', '/docs/references/auth/recovery-reset.md')
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', function () { return new UID(); }, 'User account email address.')
->param('token', '', function () { return new Text(256); }, 'Valid reset token.')
->param('password-a', '', function () { return new Password(); }, 'New password.')
->param('password-b', '', function () {return new Password(); }, 'New password again.')
->action(
function ($userId, $token, $passwordA, $passwordB) use ($response, $projectDB, $audit) {
if ($passwordA !== $passwordB) {
throw new Exception('Passwords must match', 400);
}
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'$uid='.$userId,
],
]);
if (empty($profile)) {
throw new Exception('User not found', 404); // TODO maybe hide this
}
$token = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_RECOVERY, $token);
if (!$token) {
throw new Exception('Recovery token is not valid', 401);
}
Authorization::setRole('user:'.$profile->getUid());
$profile = $projectDB->updateDocument(array_merge($profile->getArrayCopy(), [
'password' => Auth::passwordHash($passwordA),
'password-update' => time(),
'confirm' => true,
]));
if (false === $profile) {
throw new Exception('Failed saving user to DB', 500);
}
if (!$projectDB->deleteDocument($token)) {
throw new Exception('Failed to remove token from DB', 500);
}
$audit
->setParam('userId', $profile->getUid())
->setParam('event', 'auth.recovery.reset')
;
$response->json(array('result' => 'success'));
}
);

View file

@ -103,11 +103,11 @@ $utopia->get('/v1/projects')
foreach ($results as $project) {
foreach ($providers as $provider => $node) {
$secret = json_decode($project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}'), true);
$secret = json_decode($project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}'), true);
if (!empty($secret) && isset($secret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$secret['version']);
$project->setAttribute('usersOauth'.ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, hex2bin($secret['iv']), hex2bin($secret['tag'])));
$project->setAttribute('usersOauth2'.ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, hex2bin($secret['iv']), hex2bin($secret['tag'])));
}
}
}
@ -131,11 +131,11 @@ $utopia->get('/v1/projects/:projectId')
}
foreach ($providers as $provider => $node) {
$secret = json_decode($project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}'), true);
$secret = json_decode($project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}'), true);
if (!empty($secret) && isset($secret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$secret['version']);
$project->setAttribute('usersOauth'.ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, hex2bin($secret['iv']), hex2bin($secret['tag'])));
$project->setAttribute('usersOauth2'.ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, hex2bin($secret['iv']), hex2bin($secret['tag'])));
}
}
@ -322,11 +322,11 @@ $utopia->patch('/v1/projects/:projectId')
}
);
$utopia->patch('/v1/projects/:projectId/oauth')
->desc('Update Project OAuth')
$utopia->patch('/v1/projects/:projectId/oauth2')
->desc('Update Project OAuth2')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateOAuth')
->label('sdk.method', 'updateOAuth2')
->param('projectId', '', function () { return new UID(); }, 'Project unique ID.')
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'Provider Name', false)
->param('appId', '', function () { return new Text(256); }, 'Provider app ID.', true)
@ -351,8 +351,8 @@ $utopia->patch('/v1/projects/:projectId/oauth')
]);
$project = $consoleDB->updateDocument(array_merge($project->getArrayCopy(), [
'usersOauth'.ucfirst($provider).'Appid' => $appId,
'usersOauth'.ucfirst($provider).'Secret' => $secret,
'usersOauth2'.ucfirst($provider).'Appid' => $appId,
'usersOauth2'.ucfirst($provider).'Secret' => $secret,
]));
if (false === $project) {

View file

@ -62,15 +62,15 @@ $utopia->post('/v1/users')
'name' => $name,
]);
$oauthKeys = [];
$oauth2Keys = [];
foreach ($providers as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauthKeys[] = 'oauth'.ucfirst($key);
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
$oauth2Keys[] = 'oauth2'.ucfirst($key);
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
}
$response
@ -82,7 +82,7 @@ $utopia->post('/v1/users')
'registration',
'emailVerification',
'name',
], $oauthKeys)), ['roles' => []]));
], $oauth2Keys)), ['roles' => []]));
}
);
@ -111,18 +111,18 @@ $utopia->get('/v1/users')
],
]);
$oauthKeys = [];
$oauth2Keys = [];
foreach ($providers as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauthKeys[] = 'oauth'.ucfirst($key);
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
$oauth2Keys[] = 'oauth2'.ucfirst($key);
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
}
$results = array_map(function ($value) use ($oauthKeys) { /* @var $value \Database\Document */
$results = array_map(function ($value) use ($oauth2Keys) { /* @var $value \Database\Document */
return $value->getArrayCopy(array_merge(
[
'$uid',
@ -132,7 +132,7 @@ $utopia->get('/v1/users')
'emailVerification',
'name',
],
$oauthKeys
$oauth2Keys
));
}, $results);
@ -156,15 +156,15 @@ $utopia->get('/v1/users/:userId')
throw new Exception('User not found', 404);
}
$oauthKeys = [];
$oauth2Keys = [];
foreach ($providers as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauthKeys[] = 'oauth'.ucfirst($key);
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
$oauth2Keys[] = 'oauth2'.ucfirst($key);
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
}
$response->json(array_merge($user->getArrayCopy(array_merge(
@ -176,7 +176,7 @@ $utopia->get('/v1/users/:userId')
'emailVerification',
'name',
],
$oauthKeys
$oauth2Keys
)), ['roles' => []]));
}
);
@ -362,15 +362,15 @@ $utopia->patch('/v1/users/:userId/status')
throw new Exception('Failed saving user to DB', 500);
}
$oauthKeys = [];
$oauth2Keys = [];
foreach ($providers as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauthKeys[] = 'oauth'.ucfirst($key);
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
$oauth2Keys[] = 'oauth2'.ucfirst($key);
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
}
$response
@ -381,7 +381,7 @@ $utopia->patch('/v1/users/:userId/status')
'registration',
'emailVerification',
'name',
], $oauthKeys)), ['roles' => []]));
], $oauth2Keys)), ['roles' => []]));
}
);

View file

@ -235,28 +235,28 @@ $utopia->get('/v1/mock/tests/general/empty')
}
);
$utopia->get('/v1/mock/tests/general/oauth')
->desc('Mock an OAuth login route')
$utopia->get('/v1/mock/tests/general/oauth2')
->desc('Mock an OAuth2 login route')
->label('scope', 'public')
->label('docs', false)
->param('client_id', '', function () { return new Text(100); }, 'OAuth Client ID.')
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth Redirect URI.') // Important to deny an open redirect attack
->param('scope', '', function () { return new Text(100); }, 'OAuth scope list.')
->param('state', '', function () { return new Text(1024); }, 'OAuth state.')
->param('client_id', '', function () { return new Text(100); }, 'OAuth2 Client ID.')
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth2 Redirect URI.') // Important to deny an open redirect attack
->param('scope', '', function () { return new Text(100); }, 'OAuth2 scope list.')
->param('state', '', function () { return new Text(1024); }, 'OAuth2 state.')
->action(
function ($clientId, $redirectURI, $scope, $state) use ($response) {
$response->redirect($redirectURI.'?'.http_build_query(['code' => 'abcdef', 'state' => $state]));
}
);
$utopia->get('/v1/mock/tests/general/oauth/token')
->desc('Mock an OAuth login route')
$utopia->get('/v1/mock/tests/general/oauth2/token')
->desc('Mock an OAuth2 login route')
->label('scope', 'public')
->label('docs', false)
->param('client_id', '', function () { return new Text(100); }, 'OAuth Client ID.')
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth Redirect URI.')
->param('client_secret', '', function () { return new Text(100); }, 'OAuth scope list.')
->param('code', '', function () { return new Text(100); }, 'OAuth state.')
->param('client_id', '', function () { return new Text(100); }, 'OAuth2 Client ID.')
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth2 Redirect URI.')
->param('client_secret', '', function () { return new Text(100); }, 'OAuth2 scope list.')
->param('code', '', function () { return new Text(100); }, 'OAuth2 state.')
->action(
function ($clientId, $redirectURI, $clientSecret, $code) use ($response) {
if($clientId != '1') {
@ -275,11 +275,11 @@ $utopia->get('/v1/mock/tests/general/oauth/token')
}
);
$utopia->get('/v1/mock/tests/general/oauth/user')
->desc('Mock an OAuth user route')
$utopia->get('/v1/mock/tests/general/oauth2/user')
->desc('Mock an OAuth2 user route')
->label('scope', 'public')
->label('docs', false)
->param('token', '', function () { return new Text(100); }, 'OAuth Access Token.')
->param('token', '', function () { return new Text(100); }, 'OAuth2 Access Token.')
->action(
function ($token) use ($response) {
if($token != '123456') {
@ -294,7 +294,7 @@ $utopia->get('/v1/mock/tests/general/oauth/user')
}
);
$utopia->get('/v1/mock/tests/general/oauth/success')
$utopia->get('/v1/mock/tests/general/oauth2/success')
->label('scope', 'public')
->label('docs', false)
->action(
@ -305,7 +305,7 @@ $utopia->get('/v1/mock/tests/general/oauth/success')
}
);
$utopia->get('/v1/mock/tests/general/oauth/failure')
$utopia->get('/v1/mock/tests/general/oauth2/failure')
->label('scope', 'public')
->label('docs', false)
->action(

View file

@ -37,7 +37,7 @@ $response = new Response();
$env = $request->getServer('_APP_ENV', App::ENV_TYPE_PRODUCTION);
$domain = $request->getServer('HTTP_HOST', '');
$version = $request->getServer('_APP_VERSION', 'UNKNOWN');
$providers = include __DIR__.'/../app/config/providers.php'; // OAuth providers list
$providers = include __DIR__.'/../app/config/providers.php'; // OAuth2 providers list
$platforms = include __DIR__.'/../app/config/platforms.php';
$locales = include __DIR__.'/../app/config/locales.php'; // Locales list
$collections = include __DIR__.'/../app/config/collections.php'; // Collections list

View file

@ -184,8 +184,8 @@ class Account extends Service {
/// choice. Each OAuth provider should be enabled from the Appwrite console
/// first. Use the success and failure arguments to provide a redirect URL's
/// back to your app when login is completed.
Future<Response> createOAuthSession({provider, success, failure}) async {
String path = '/account/sessions/oauth/{provider}'.replaceAll(RegExp('{provider}'), provider);
Future<Response> createOAuth2Session({provider, success, failure}) async {
String path = '/account/sessions/oauth2/{provider}'.replaceAll(RegExp('{provider}'), provider);
Map<String, dynamic> params = {
'success': success,

View file

@ -0,0 +1,9 @@
let sdk = new Appwrite();
sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.account.createOAuth2Session('bitbucket', 'https://example.com', 'https://example.com');
console.log(result); // Resource URL

View file

@ -746,7 +746,7 @@
},
/**
* Create Account Session with OAuth
* Create Account Session with OAuth2
*
* Allow the user to login to his account using the OAuth provider of his
* choice. Each OAuth provider should be enabled from the Appwrite console
@ -759,7 +759,7 @@
* @throws {Error}
* @return {string}
*/
createOAuthSession: function(provider, success, failure) {
createOAuth2Session: function(provider, success, failure) {
if(provider === undefined) {
throw new Error('Missing required parameter: "provider"');
}
@ -772,7 +772,7 @@
throw new Error('Missing required parameter: "failure"');
}
let path = '/account/sessions/oauth/{provider}'.replace(new RegExp('{provider}', 'g'), provider);
let path = '/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}', 'g'), provider);
let payload = {};

View file

@ -44,10 +44,10 @@ return http.put(path,{'content-type':'application/json',},payload)},getSessions:
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account/sessions';let payload={};if(email){payload.email=email}
if(password){payload.password=password}
return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuthSession:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
if(success===undefined){throw new Error('Missing required parameter: "success"')}
if(failure===undefined){throw new Error('Missing required parameter: "failure"')}
let path='/account/sessions/oauth/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload.success=success}
let path='/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload.success=success}
if(failure){payload.failure=failure}
payload.project=config.project;let query=Object.keys(payload).map(key=>key+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"')}
let path='/account/sessions/{sessionId}'.replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createVerification:function(url){if(url===undefined){throw new Error('Missing required parameter: "url"')}

View file

@ -6,7 +6,7 @@ $home = $this->getParam('home', '');
<div class="margin-top margin-bottom">
<a href="/">
<img src="/images/appwrite.svg" alt="Appwrite Light Logo" class="force-light" height="30" />
<img src="/images/appwrite-footer.svg" alt="Appwrite Dark Logo" class="force-dark" height="30" />
<img src="/images/appwrite-footer-dark.svg" alt="Appwrite Dark Logo" class="force-dark" height="30" />
</a>
</div>

View file

@ -318,7 +318,7 @@ $providers = $this->getParam('providers', []);
</li>
<li data-state="/console/users/providers?project={{router.params.project}}">
<h2>OAuth Providers</h2>
<h2>OAuth2 Providers</h2>
<div class="box margin-bottom margin-top-large"
data-service="projects.get"
@ -335,39 +335,39 @@ $providers = $this->getParam('providers', []);
<div data-ui-modal class="modal close" data-button-text="Settings" data-button-class="pull-end">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1><?php echo ucfirst($provider); ?> OAuth Settings</h1>
<h1><?php echo ucfirst($provider); ?> OAuth2 Settings</h1>
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project OAuth"
data-service="projects.updateOAuth"
data-analytics-label="Update Project OAuth2"
data-service="projects.updateOAuth2"
data-scope="console"
data-event="submit"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Updated project OAuth settings successfully"
data-success-param-alert-text="Updated project OAuth2 settings successfully"
data-success-param-trigger-events="projects.update,modal-close"
data-failure="alert"
data-failure-param-alert-text="Failed to update project OAuth settings"
data-failure-param-alert-text="Failed to update project OAuth2 settings"
data-failure-param-alert-classname="error">
<input name="provider" id="provider<?php echo ucfirst($provider); ?>" type="hidden" autocomplete="off" value="<?php echo $provider; ?>">
<label for="oauth<?php echo ucfirst($provider); ?>Appid">App ID</label>
<input name="appId" id="oauth<?php echo ucfirst($provider); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth<?php echo ucfirst($provider); ?>Appid}}">
<label for="oauth2<?php echo ucfirst($provider); ?>Appid">App ID</label>
<input name="appId" id="oauth2<?php echo ucfirst($provider); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Appid}}">
<label for="oauth<?php echo ucfirst($provider); ?>Secret">App Secret</label>
<input name="secret" id="oauth<?php echo ucfirst($provider); ?>Secret" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth<?php echo ucfirst($provider); ?>Secret}}">
<label for="oauth2<?php echo ucfirst($provider); ?>Secret">App Secret</label>
<input name="secret" id="oauth2<?php echo ucfirst($provider); ?>Secret" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Secret}}">
<div class="info row thin margin-bottom margin-top">
<div class="col span-1">
<i class="icon-info-circled text-sign"></i>
</div>
<div class="col span-11">
<p>To complete set up, add this OAuth redirect URI to your <?php echo ucfirst($provider); ?> app configuration.</p>
<p>To complete set up, add this OAuth2 redirect URI to your <?php echo ucfirst($provider); ?> app configuration.</p>
<div class="input-copy">
<input data-forms-copy type="text" disabled data-ls-bind="{{env.PROTOCOL}}://{{env.DOMAIN}}/v1/account/sessions/oauth/callback/<?php echo $provider; ?>/{{router.params.project}}" class="margin-bottom-no" />
<input data-forms-copy type="text" disabled data-ls-bind="{{env.PROTOCOL}}://{{env.DOMAIN}}/v1/account/sessions/oauth2/callback/<?php echo $provider; ?>/{{router.params.project}}" class="margin-bottom-no" />
</div>
</div>
</div>
@ -376,17 +376,17 @@ $providers = $this->getParam('providers', []);
</form>
</div>
<img src="/images/oauth/<?php echo strtolower($provider); ?>.png" alt="<?php echo ucfirst($provider); ?> Logo" class="pull-start provider margin-end" />
<img src="/images/oauth2/<?php echo strtolower($provider); ?>.png" alt="<?php echo ucfirst($provider); ?> Logo" class="pull-start provider margin-end" />
<span>
<?php echo ucfirst($provider); ?>
<span data-ls-if="
!{{console-project.usersOauth<?php echo ucfirst($provider); ?>Appid}} ||
!{{console-project.usersOauth<?php echo ucfirst($provider); ?>Secret}}">
!{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Appid}} ||
!{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Secret}}">
&nbsp;<span class="tag red">Disabled</span>
</span>
</span>
<p class="margin-bottom-no margin-top-small text-one-liner">
<a href="<?php echo $data['developers']; ?>" target="_blank" rel="noopener">OAuth Developer Docs<i class="icon-link-ext"></i></a>
<a href="<?php echo $data['developers']; ?>" target="_blank" rel="noopener">OAuth2 Developer Docs<i class="icon-link-ext"></i></a>
</p>
</li>
<?php endforeach; ?>

View file

@ -1 +0,0 @@
Allow the user to login to his account using the OAuth provider of his choice. Each OAuth provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.

View file

@ -0,0 +1 @@
Allow the user to login to his account using the OAuth2 provider of his choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.

View file

@ -1,4 +1,4 @@
# Adding a New OAuth Provider
# Adding a New OAuth2 Provider
This document is part of the Appwrite contributors' guide. Before you continue reading this document make sure you have read the [Code of Conduct](../CODE_OF_CONDUCT.md) and the [Contributing Guide](../CONTRIBUTING.md).
@ -6,13 +6,13 @@ This document is part of the Appwrite contributors' guide. Before you continue r
### Agenda
OAuth providers help users to log in easily to apps and websites without the need to provide passwords or any other type of credentials. Appwrite's goal is to have support from as many **major** OAuth providers as possible.
OAuth2 providers help users to log in easily to apps and websites without the need to provide passwords or any other type of credentials. Appwrite's goal is to have support from as many **major** OAuth2 providers as possible.
As of the writing of these lines, we do not accept any minor OAuth providers. For us to accept some smaller and potentially unlimited number of OAuth providers, some product design and software architecture changes must be applied first.
As of the writing of these lines, we do not accept any minor OAuth2 providers. For us to accept some smaller and potentially unlimited number of OAuth2 providers, some product design and software architecture changes must be applied first.
### List Your new Provider
The first step in adding a new OAuth provider is to add it to the list in providers config file array, located at:
The first step in adding a new OAuth2 provider is to add it to the list in providers config file array, located at:
```
./app/config/providers.php
@ -25,13 +25,13 @@ Make sure to fill all data needed and that your provider array key name:
### Add Provider Logo
Add a logo image to your new provider in this path: `./public/images/oauth`. Your logo should be a png 100×100px file with the name of your provider (all lowercase). Please make sure to leave about 30px padding around the logo to be consistent with other logos.
Add a logo image to your new provider in this path: `./public/images/oauth2`. Your logo should be a png 100×100px file with the name of your provider (all lowercase). Please make sure to leave about 30px padding around the logo to be consistent with other logos.
### Add Provider Class
Once you have finished setting up all the metadata for the new provider, you need to start coding.
Create a new class that extends the basic OAuth provider abstract class in this location:
Create a new class that extends the basic OAuth2 provider abstract class in this location:
```bash
./src/Auth/OAuth/ProviderName
@ -41,7 +41,7 @@ Note that the class name should start with a capital letter as PHP FIG standards
Once a new class is created, you can start to implement your new provider's login flow. The best way to do this correctly is to have a look at another provider's implementation and try to follow the same standards.
Please mention in your documentation what resources or API docs you used to implement the provider's OAuth protocol.
Please mention in your documentation what resources or API docs you used to implement the provider's OAuth2 protocol.
### Test Your Provider
@ -49,7 +49,7 @@ After you finished adding your new provider to Appwrite you should be able to se
Add credentials and check both a successful and a failed login (where the user rejects integration on provider page).
You can test your OAuth provider by trying to login using the [OAuth method](https://appwrite.io/docs/auth#oauth) when integrating the Appwrite JS SDK in a demo app.
You can test your OAuth2 provider by trying to login using the [OAuth2 method](https://appwrite.io/docs/account#createOAuth2Session) when integrating the Appwrite JS SDK in a demo app.
Pass your new adapter name as the provider parameter. If login is successful, you will be redirected to your success URL parameter. Otherwise, you will be redirected to your failure URL.

View file

@ -50,10 +50,10 @@ return http.put(path,{'content-type':'application/json',},payload);},getSessions
if(password===undefined){throw new Error('Missing required parameter: "password"');}
let path='/account/sessions';let payload={};if(email){payload['email']=email;}
if(password){payload['password']=password;}
return http.post(path,{'content-type':'application/json',},payload);},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload);},createOAuthSession:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"');}
return http.post(path,{'content-type':'application/json',},payload);},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload);},createOAuth2Session:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"');}
if(success===undefined){throw new Error('Missing required parameter: "success"');}
if(failure===undefined){throw new Error('Missing required parameter: "failure"');}
let path='/account/sessions/oauth/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload['success']=success;}
let path='/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload['success']=success;}
if(failure){payload['failure']=failure;}
return http.get(path,{'content-type':'application/json',},payload);},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "id"');}
let path='/account/sessions/{sessionId}'.replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},createVerification:function(url){if(url===undefined){throw new Error('Missing required parameter: "url"');}
@ -186,9 +186,9 @@ let path='/projects/{projectId}/keys/{keyId}'.replace(new RegExp('{projectId}','
if(scopes){payload['scopes']=scopes;}
return http.put(path,{'content-type':'application/json',},payload);},deleteKey:function(projectId,keyId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(keyId===undefined){throw new Error('Missing required parameter: "keyId"');}
let path='/projects/{projectId}/keys/{keyId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{keyId}','g'),keyId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},updateOAuth:function(projectId,provider,appId='',secret=''){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/keys/{keyId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{keyId}','g'),keyId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},updateOAuth2:function(projectId,provider,appId='',secret=''){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(provider===undefined){throw new Error('Missing required parameter: "provider"');}
let path='/projects/{projectId}/oauth'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(provider){payload['provider']=provider;}
let path='/projects/{projectId}/oauth2'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(provider){payload['provider']=provider;}
if(appId){payload['appId']=appId;}
if(secret){payload['secret']=secret;}
return http.patch(path,{'content-type':'application/json',},payload);},listPlatforms:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}

View file

@ -50,10 +50,10 @@ return http.put(path,{'content-type':'application/json',},payload);},getSessions
if(password===undefined){throw new Error('Missing required parameter: "password"');}
let path='/account/sessions';let payload={};if(email){payload['email']=email;}
if(password){payload['password']=password;}
return http.post(path,{'content-type':'application/json',},payload);},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload);},createOAuthSession:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"');}
return http.post(path,{'content-type':'application/json',},payload);},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload);},createOAuth2Session:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"');}
if(success===undefined){throw new Error('Missing required parameter: "success"');}
if(failure===undefined){throw new Error('Missing required parameter: "failure"');}
let path='/account/sessions/oauth/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload['success']=success;}
let path='/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload['success']=success;}
if(failure){payload['failure']=failure;}
return http.get(path,{'content-type':'application/json',},payload);},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "id"');}
let path='/account/sessions/{sessionId}'.replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},createVerification:function(url){if(url===undefined){throw new Error('Missing required parameter: "url"');}
@ -186,9 +186,9 @@ let path='/projects/{projectId}/keys/{keyId}'.replace(new RegExp('{projectId}','
if(scopes){payload['scopes']=scopes;}
return http.put(path,{'content-type':'application/json',},payload);},deleteKey:function(projectId,keyId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(keyId===undefined){throw new Error('Missing required parameter: "keyId"');}
let path='/projects/{projectId}/keys/{keyId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{keyId}','g'),keyId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},updateOAuth:function(projectId,provider,appId='',secret=''){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/keys/{keyId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{keyId}','g'),keyId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},updateOAuth2:function(projectId,provider,appId='',secret=''){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(provider===undefined){throw new Error('Missing required parameter: "provider"');}
let path='/projects/{projectId}/oauth'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(provider){payload['provider']=provider;}
let path='/projects/{projectId}/oauth2'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(provider){payload['provider']=provider;}
if(appId){payload['appId']=appId;}
if(secret){payload['secret']=secret;}
return http.patch(path,{'content-type':'application/json',},payload);},listPlatforms:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}

View file

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -771,10 +771,10 @@
},
/**
* Create Account Session with OAuth
* Create Account Session with OAuth2
*
* Allow the user to login to his account using the OAuth provider of his
* choice. Each OAuth provider should be enabled from the Appwrite console
* Allow the user to login to his account using the OAuth2 provider of his
* choice. Each OAuth2 provider should be enabled from the Appwrite console
* first. Use the success and failure arguments to provide a redirect URL's
* back to your app when login is completed.
*
@ -784,7 +784,7 @@
* @throws {Error}
* @return {Promise}
*/
createOAuthSession: function(provider, success, failure) {
createOAuth2Session: function(provider, success, failure) {
if(provider === undefined) {
throw new Error('Missing required parameter: "provider"');
}
@ -797,7 +797,7 @@
throw new Error('Missing required parameter: "failure"');
}
let path = '/account/sessions/oauth/{provider}'.replace(new RegExp('{provider}', 'g'), provider);
let path = '/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}', 'g'), provider);
let payload = {};
@ -2161,7 +2161,7 @@
},
/**
* Update Project OAuth
* Update Project OAuth2
*
*
* @param {string} projectId
@ -2171,7 +2171,7 @@
* @throws {Error}
* @return {Promise}
*/
updateOAuth: function(projectId, provider, appId = '', secret = '') {
updateOAuth2: function(projectId, provider, appId = '', secret = '') {
if(projectId === undefined) {
throw new Error('Missing required parameter: "projectId"');
}
@ -2180,7 +2180,7 @@
throw new Error('Missing required parameter: "provider"');
}
let path = '/projects/{projectId}/oauth'.replace(new RegExp('{projectId}', 'g'), projectId);
let path = '/projects/{projectId}/oauth2'.replace(new RegExp('{projectId}', 'g'), projectId);
let payload = {};

View file

@ -2,7 +2,7 @@
namespace Auth;
abstract class OAuth
abstract class OAuth2
{
/**
* @var string
@ -30,7 +30,7 @@ abstract class OAuth
protected $scopes;
/**
* OAuth constructor.
* OAuth2 constructor.
*
* @param string $appId
* @param string $appSecret
@ -92,7 +92,7 @@ abstract class OAuth
*
* @return $this
*/
protected function addScope(string $scope):OAuth
protected function addScope(string $scope):OAuth2
{
// Add a scope to the scopes array if it isn't already present
if (!in_array($scope, $this->scopes)){
@ -110,7 +110,7 @@ abstract class OAuth
}
// The parseState function was designed specifically for Amazon OAuth Adapter to override.
// The parseState function was designed specifically for Amazon OAuth2 Adapter to override.
// The response from Amazon is html encoded and hence it needs to be html_decoded before
// json_decoding
/**
@ -138,7 +138,7 @@ abstract class OAuth
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Console_OAuth_Agent');
curl_setopt($ch, CURLOPT_USERAGENT, APP_USERAGENT);
if (!empty($payload)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);

View file

@ -1,14 +1,15 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://developer.amazon.com/docs/login-with-amazon/authorization-code-grant.html
// https://developer.amazon.com/docs/login-with-amazon/register-web.html
// https://developer.amazon.com/docs/login-with-amazon/obtain-customer-profile.html
class Amazon extends OAuth
class Amazon extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
class Apple extends OAuth
class Apple extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html#OAuthonBitbucketCloud-Createaconsumer
class Bitbucket extends OAuth
class Bitbucket extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://dev.bitly.com/v4_documentation.html
//
class Bitly extends OAuth
class Bitly extends OAuth2
{
/**

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://discordapp.com/developers/docs/topics/oauth2
class Discord extends OAuth
class Discord extends OAuth2
{
/**
* @var string

View file

@ -1,13 +1,14 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://www.dropbox.com/developers/reference/oauth-guide
// https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
class Dropbox extends OAuth
class Dropbox extends OAuth2
{
/**
* @var array

View file

@ -1,10 +1,10 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
class Facebook extends OAuth
class Facebook extends OAuth2
{
/**
* @var string

View file

@ -1,10 +1,10 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
class Github extends OAuth
class Github extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://docs.gitlab.com/ee/api/oauth2.html
class Gitlab extends OAuth
class Gitlab extends OAuth2
{
/**
* @var array

View file

@ -1,14 +1,15 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://developers.google.com/oauthplayground/
// https://developers.google.com/identity/protocols/OAuth2
// https://developers.google.com/identity/protocols/OAuth2WebServer
class Google extends OAuth
class Google extends OAuth2
{
/**
* @var string

View file

@ -1,10 +1,10 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
class LinkedIn extends OAuth
class LinkedIn extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,14 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
// https://docs.microsoft.com/en-us/graph/auth-v2-user
class Microsoft extends OAuth
class Microsoft extends OAuth2
{
/**
* @var array

View file

@ -1,10 +1,10 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
class Mock extends OAuth
class Mock extends OAuth2
{
/**
* @var string
@ -36,7 +36,7 @@ class Mock extends OAuth
*/
public function getLoginURL():string
{
return 'http://localhost/'.$this->version.'/mock/tests/general/oauth?'. http_build_query([
return 'http://localhost/'.$this->version.'/mock/tests/general/oauth2?'. http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => implode(' ', $this->getScopes()),
@ -53,7 +53,7 @@ class Mock extends OAuth
{
$accessToken = $this->request(
'GET',
'http://localhost/'.$this->version.'/mock/tests/general/oauth/token?'.
'http://localhost/'.$this->version.'/mock/tests/general/oauth2/token?'.
http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
@ -127,7 +127,7 @@ class Mock extends OAuth
protected function getUser(string $accessToken):array
{
if (empty($this->user)) {
$user = $this->request('GET', 'http://localhost/'.$this->version.'/mock/tests/general/oauth/user?token='.urlencode($accessToken));
$user = $this->request('GET', 'http://localhost/'.$this->version.'/mock/tests/general/oauth2/user?token='.urlencode($accessToken));
$this->user = json_decode($user, true);
}

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://developer.paypal.com/docs/api/overview/
class Paypal extends OAuth
class Paypal extends OAuth2
{
/**
* @var string

View file

@ -1,15 +1,15 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://help.salesforce.com/articleView?id=remoteaccess_oauth_endpoints.htm&type=5
// https://help.salesforce.com/articleView?id=remoteaccess_oauth_tokens_scopes.htm&type=5
// https://help.salesforce.com/articleView?id=remoteaccess_oauth_web_server_flow.htm&type=5
class Salesforce extends OAuth
class Salesforce extends OAuth2
{
/**
* @var array

View file

@ -1,10 +1,10 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
class Slack extends OAuth
class Slack extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://dev.twitch.tv/docs/authentication
class Spotify extends OAuth
class Spotify extends OAuth2
{
/**

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://dev.twitch.tv/docs/authentication
class Twitch extends OAuth
class Twitch extends OAuth2
{
/**

View file

@ -1,8 +1,8 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://vk.com/dev/first_guide
@ -10,7 +10,7 @@ use Auth\OAuth;
// https://vk.com/dev/api_requests
// https://plugins.miniorange.com/guide-to-configure-vkontakte-as-oauth-server
class Vk extends OAuth
class Vk extends OAuth2
{
/**
* @var array

View file

@ -1,13 +1,13 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://developer.yahoo.com/oauth2/guide/
class Yahoo extends OAuth
class Yahoo extends OAuth2
{
/**

View file

@ -1,15 +1,15 @@
<?php
namespace Auth\OAuth;
namespace Auth\OAuth2;
use Auth\OAuth;
use Auth\OAuth2;
// Reference Material
// https://tech.yandex.com/passport/doc/dg/reference/request-docpage/
// https://tech.yandex.com/oauth/doc/dg/reference/web-client-docpage/
class Yandex extends OAuth
class Yandex extends OAuth2
{
/**
* @var array

View file

@ -361,7 +361,7 @@ trait AccountBase
return $data;
}
//TODO Add tests for OAuth session creation
//TODO Add tests for OAuth2 session creation
/**
* @depends testCreateAccountSession

View file

@ -13,7 +13,7 @@ class AccountCustomClientTest extends Scope
use ProjectCustom;
use SideClient;
public function testCreateOAuthAccountSession():array
public function testCreateOAuth2AccountSession():array
{
$provider = 'mock';
$appId = '1';
@ -22,7 +22,7 @@ class AccountCustomClientTest extends Scope
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$this->getProject()['$uid'].'/oauth', array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$this->getProject()['$uid'].'/oauth2', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
@ -35,13 +35,13 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth/'.$provider, array_merge([
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/'.$provider, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$uid'],
]), [
'success' => 'http://localhost/v1/mock/tests/general/oauth/success',
'failure' => 'http://localhost/v1/mock/tests/general/oauth/failure',
'success' => 'http://localhost/v1/mock/tests/general/oauth2/success',
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
]);
$this->assertEquals(200, $response['headers']['status-code']);