1
0
Fork 0
mirror of synced 2024-06-27 02:31:04 +12:00

Merge pull request #2733 from appwrite/feat-oauth-refresh-tokens

Feat oauth refresh tokens
This commit is contained in:
Eldad A. Fux 2022-02-04 14:42:28 +02:00 committed by GitHub
commit e42c012961
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 1453 additions and 418 deletions

View file

@ -1158,7 +1158,29 @@ $collections = [
'filters' => [],
],
[
'$id' => 'providerToken',
'$id' => 'providerAccessToken',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
[
'$id' => 'providerAccessTokenExpiry',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'providerRefreshToken',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,

View file

@ -82,6 +82,11 @@ return [
'model' => Response::MODEL_SESSION,
'note' => '',
],
'account.sessions.update' => [
'description' => 'This event triggers when the account session is updated.',
'model' => Response::MODEL_SESSION,
'note' => '',
],
'database.collections.create' => [
'description' => 'This event triggers when a database collection is created.',
'model' => Response::MODEL_COLLECTION,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -23,6 +23,7 @@ use Utopia\Database\Validator\UID;
use Utopia\Exception;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@ -430,7 +431,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
$state['failure'] = null;
$accessToken = $oauth2->getAccessToken($code);
$refreshToken =$oauth2->getRefreshToken($code);
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code);
if (empty($accessToken)) {
if (!empty($state['failure'])) {
@ -528,7 +532,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'userId' => $user->getId(),
'provider' => $provider,
'providerUid' => $oauth2ID,
'providerToken' => $accessToken,
'providerAccessToken' => $accessToken,
'providerRefreshToken' => $refreshToken,
'providerAccessTokenExpiry' => \time() + (int) $accessTokenExpiry,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -1198,6 +1204,7 @@ App::get('/v1/account/logs')
'account.update.password',
'account.update.prefs',
'account.sessions.create',
'account.sessions.update',
'account.sessions.delete',
'account.recovery.create',
'account.recovery.update',
@ -1593,8 +1600,8 @@ App::delete('/v1/account/sessions/:sessionId')
$protocol = $request->getProtocol();
$sessionId = ($sessionId === 'current')
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId;
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId;
$sessions = $user->getAttribute('sessions', []);
@ -1647,6 +1654,108 @@ App::delete('/v1/account/sessions/:sessionId')
throw new Exception('Session not found', 404);
});
App::patch('/v1/account/sessions/:sessionId')
->desc('Update Session (Refresh Tokens)')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'account.sessions.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateSession')
->label('sdk.description', '/docs/references/account/update-session.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 10)
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to update the current device session.')
->inject('request')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('project')
->inject('locale')
->inject('audits')
->inject('events')
->inject('usage')
->action(function ($sessionId, $request, $response, $user, $dbForProject, $project, $locale, $audits, $events, $usage) {
/** @var Appwrite\Utopia\Request $request */
/** @var boolean $force */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
$sessionId = ($sessionId === 'current')
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId;
$sessions = $user->getAttribute('sessions', []);
foreach ($sessions as $key => $session) {/** @var Document $session */
if ($sessionId == $session->getId()) {
// Comment below would skip re-generation if token is still valid
// We decided to not include this because developer can get expiration date from the session
// I kept code in comment because it might become relevant in the future
// $expireAt = (int) $session->getAttribute('providerAccessTokenExpiry');
// if(\time() < $expireAt - 5) { // 5 seconds time-sync and networking gap, to be safe
// return $response->noContent();
// }
$provider = $session->getAttribute('provider');
$refreshToken = $session->getAttribute('providerRefreshToken');
$appId = $project->getAttribute('providers', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('providers', [])[$provider.'Secret'] ?? '{}';
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
if (!\class_exists($className)) {
throw new Exception('Provider is not supported', 501);
}
$oauth2 = new $className($appId, $appSecret, '', [], []);
$oauth2->refreshTokens($refreshToken);
$session
->setAttribute('providerAccessToken', $oauth2->getAccessToken(''))
->setAttribute('providerRefreshToken', $oauth2->getRefreshToken(''))
->setAttribute('providerAccessTokenExpiry', \time() + (int) $oauth2->getAccessTokenExpiry(''))
;
$dbForProject->updateDocument('sessions', $sessionId, $session);
$user->setAttribute("sessions", $sessions);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.sessions.update')
->setParam('resource', 'user/' . $user->getId())
;
$events
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
;
$usage
->setParam('users.sessions.update', 1)
->setParam('users.update', 1)
;
return $response->dynamic($session, Response::MODEL_SESSION);
}
}
throw new Exception('Session not found', 404);
});
App::delete('/v1/account/sessions')
->desc('Delete All Account Sessions')
->groups(['api', 'account'])

View file

@ -289,6 +289,7 @@ App::get('/v1/users/:userId/logs')
'account.update.password',
'account.update.prefs',
'account.sessions.create',
'account.sessions.update',
'account.sessions.delete',
'account.recovery.create',
'account.recovery.update',

View file

@ -10,6 +10,7 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Integer;
use Utopia\Validator\Text;
use Utopia\Storage\Validator\File;
use Utopia\Validator\WhiteList;
App::get('/v1/mock/tests/foo')
->desc('Get Foo')
@ -478,13 +479,19 @@ App::get('/v1/mock/tests/general/oauth2/token')
->label('docs', false)
->label('sdk.mock', true)
->param('client_id', '', new Text(100), 'OAuth2 Client ID.')
->param('redirect_uri', '', new Host(['localhost']), 'OAuth2 Redirect URI.')
->param('client_secret', '', new Text(100), 'OAuth2 scope list.')
->param('code', '', new Text(100), 'OAuth2 state.')
->param('grant_type', '', new WhiteList(['refresh_token', 'authorization_code']), 'OAuth2 Grant Type.', true)
->param('redirect_uri', '', new Host(['localhost']), 'OAuth2 Redirect URI.', true)
->param('code', '', new Text(100), 'OAuth2 state.', true)
->param('refresh_token', '', new Text(100), 'OAuth2 refresh token.', true)
->inject('response')
->action(function ($client_id, $redirectURI, $client_secret, $code, $response) {
->action(function ($client_id, $client_secret, $grantType, $redirectURI, $code, $refreshToken, $response) {
/** @var Appwrite\Utopia\Response $response */
if(empty($grantType)) {
$grantType = 'authorization_code';
}
if ($client_id != '1') {
throw new Exception('Invalid client ID');
}
@ -493,11 +500,27 @@ App::get('/v1/mock/tests/general/oauth2/token')
throw new Exception('Invalid client secret');
}
if ($code != 'abcdef') {
throw new Exception('Invalid token');
}
$responseJson = [
'access_token' => '123456',
'refresh_token' => 'tuvwxyz',
'expires_in' => 14400
];
$response->json(['access_token' => '123456']);
if($grantType === 'authorization_code') {
if ($code !== 'abcdef') {
throw new Exception('Invalid token');
}
$response->json($responseJson);
} else if($grantType === 'refresh_token') {
if ($refreshToken !== 'tuvwxyz') {
throw new Exception('Invalid refresh token');
}
$response->json($responseJson);
} else {
throw new Exception('Invalid grant type');
}
});
App::get('/v1/mock/tests/general/oauth2/user')
@ -517,7 +540,7 @@ App::get('/v1/mock/tests/general/oauth2/user')
$response->json([
'id' => 1,
'name' => 'User Name',
'email' => 'user@localhost.test',
'email' => 'useroauth@localhost.test',
]);
});

View file

@ -81,6 +81,23 @@ class [PROVIDER NAME] extends OAuth2
*/
private $endpoint = '[ENDPOINT API URL]';
/**
* @var array
*/
protected $scopes = [
// [ARRAY_OF_REQUIRED_SCOPES]
];
/**
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
*/
@ -101,14 +118,31 @@ class [PROVIDER NAME] extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
// TODO: Fire request to oauth API to generate access_token
$accessToken = "[FETCHED ACCESS TOKEN]";
return $accessToken;
if(empty($this->tokens)) {
// TODO: Fire request to oauth API to generate access_token
// Make sure to use '$this->getScopes()' to include all scopes properly
$this->tokens = "[FETCH TOKEN RESPONSE]";
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
// TODO: Fire request to oauth API to generate access_token using refresh token
$this->tokens = "[FETCH TOKEN RESPONSE]";
return $this->tokens;
}
/**
@ -118,8 +152,10 @@ class [PROVIDER NAME] extends OAuth2
*/
public function getUserID(string $accessToken): string
{
// TODO: Fetch user from oauth API and select the user ID
$userId = "[FETCHED USER ID]";
$user = $this->getUser($accessToken);
// TODO: Pick user ID from $user response
$userId = "[USER ID]";
return $userId;
}
@ -131,8 +167,10 @@ class [PROVIDER NAME] extends OAuth2
*/
public function getUserEmail(string $accessToken): string
{
// TODO: Fetch user from oauth API and select the user's email
$userEmail = "[FETCHED USER EMAIL]";
$user = $this->getUser($accessToken);
// TODO: Pick user email from $user response
$userEmail = "[USER EMAIL]";
return $userEmail;
}
@ -144,16 +182,35 @@ class [PROVIDER NAME] extends OAuth2
*/
public function getUserName(string $accessToken): string
{
// TODO: Fetch user from oauth API and select the username
$username = "[FETCHED USERNAME]";
$user = $this->getUser($accessToken);
// TODO: Pick username from $user response
$username = "[USERNAME]";
return $username;
}
/**
* @param string $accessToken
*
* @return array
*/
protected function getUser(string $accessToken)
{
if (empty($this->user)) {
// TODO: Fire request to oauth API to get information about users
$this->user = "[FETCH USER RESPONSE]";
}
return $this->user;
}
}
```
> If you copy this template, make sure to replace all placeholders wrapped like `[THIS]` and to implement everything marked as `TODO:`.
> If your OAuth2 provider has different endpoints for getting username/email/id, you can fire specific requests from specific get-method, and stop using `getUser` method.
Please mention in your documentation what resources or API docs you used to implement the provider's OAuth2 protocol.
## 3. Test your provider

View file

@ -62,9 +62,16 @@ abstract class OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
abstract public function getAccessToken(string $code):string;
abstract protected function getTokens(string $code):array;
/**
* @param string $refreshToken
*
* @return array
*/
abstract public function refreshTokens(string $refreshToken):array;
/**
* @param $accessToken
@ -109,6 +116,38 @@ abstract class OAuth2
return $this->scopes;
}
/**
* @param string $code
*
* @return string
*/
public function getAccessToken(string $code):string
{
$tokens = $this->getTokens($code);
return $tokens['access_token'] ?? '';
}
/**
* @param string $code
*
* @return string
*/
public function getRefreshToken(string $code):string
{
$tokens = $this->getTokens($code);
return $tokens['refresh_token'] ?? '';
}
/**
* @param string $code
*
* @return string
*/
public function getAccessTokenExpiry(string $code):string
{
$tokens = $this->getTokens($code);
return $tokens['expires_in'] ?? '';
}
// 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

View file

@ -15,6 +15,11 @@ class Amazon extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -59,31 +64,54 @@ class Amazon extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
$this->tokens = \json_decode($this->request(
'POST',
'https://api.amazon.com/auth/o2/token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://api.amazon.com/auth/o2/token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID ,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback ,
'grant_type' => 'authorization_code'
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
])
);
$accessToken = \json_decode($accessToken, true);
), true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -14,6 +14,11 @@ class Apple extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -54,34 +59,60 @@ class Apple extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://appleid.apple.com/auth/token',
$headers,
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->getAppSecret(),
'redirect_uri' => $this->callback,
])
), true);
$this->claims = (isset($this->tokens['id_token'])) ? \explode('.', $this->tokens['id_token']) : [0 => '', 1 => ''];
$this->claims = (isset($this->claims[1])) ? \json_decode(\base64_decode($this->claims[1]), true) : [];
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://appleid.apple.com/auth/token',
$headers,
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getAppSecret(),
'redirect_uri' => $this->callback,
])
);
), true);
$accessToken = \json_decode($accessToken, true);
$this->claims = (isset($accessToken['id_token'])) ? \explode('.', $accessToken['id_token']) : [0 => '', 1 => ''];
$this->claims = (isset($this->claims[1])) ? \json_decode(\base64_decode($this->claims[1]), true) : [];
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
$this->claims = (isset($this->tokens['id_token'])) ? \explode('.', $this->tokens['id_token']) : [0 => '', 1 => ''];
$this->claims = (isset($this->claims[1])) ? \json_decode(\base64_decode($this->claims[1]), true) : [];
return $this->tokens;
}
/**

View file

@ -13,6 +13,11 @@ class Bitbucket extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -43,32 +48,54 @@ class Bitbucket extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
// Required as per Bitbucket Spec.
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://bitbucket.org/site/oauth2/access_token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'authorization_code'
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
// Required as per Bitbucket Spec.
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://bitbucket.org/site/oauth2/access_token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'authorization_code'
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
// Reference Material
// https://dev.bitly.com/v4_documentation.html
@ -29,6 +30,11 @@ class Bitly extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -54,31 +60,52 @@ class Bitly extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$response = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->resourceEndpoint . 'oauth/access_token',
["Content-Type: application/x-www-form-urlencoded"],
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"redirect_uri" => $this->callback,
"state" => \json_encode($this->state)
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->resourceEndpoint . 'oauth/access_token',
["Content-Type: application/x-www-form-urlencoded"],
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"redirect_uri" => $this->callback,
"state" => \json_encode($this->state)
"refresh_token" => $refreshToken,
'grant_type' => 'refresh_token'
])
);
), true);
$result = null;
if ($response) {
\parse_str($response, $result);
return $result['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -23,6 +23,11 @@ class Box extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -59,32 +64,55 @@ class Box extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$header = "Content-Type: application/x-www-form-urlencoded";
$accessToken = $this->request(
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'token',
$headers,
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"grant_type" => "authorization_code",
"scope" => \implode(',', $this->getScopes()),
"redirect_uri" => $this->callback
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'token',
[$header],
$headers,
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"grant_type" => "authorization_code",
"scope" => \implode(',', $this->getScopes()),
"redirect_uri" => $this->callback
"refresh_token" => $refreshToken,
"grant_type" => "refresh_token",
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (array_key_exists('access_token', $accessToken)) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -18,6 +18,11 @@ class Discord extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -55,31 +60,53 @@ class Discord extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth2/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'scope' => \implode(' ', $this->getScopes())
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth2/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->callback,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'scope' => \implode(' ', $this->getScopes())
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -14,6 +14,11 @@ class Dropbox extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -44,31 +49,54 @@ class Dropbox extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://api.dropboxapi.com/oauth2/token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://api.dropboxapi.com/oauth2/token',
$headers,
\http_build_query([
'code' => $code,
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
'grant_type' => 'refresh_token'
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Facebook extends OAuth2
{
@ -15,6 +16,11 @@ class Facebook extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -47,27 +53,48 @@ class Facebook extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'GET',
'https://graph.facebook.com/' . $this->version . '/oauth/access_token?' . \http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'GET',
'https://graph.facebook.com/'.$this->version.'/oauth/access_token?'.\http_build_query([
'https://graph.facebook.com/' . $this->version . '/oauth/access_token?' . \http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Github extends OAuth2
{
@ -10,6 +11,11 @@ class Github extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -42,31 +48,51 @@ class Github extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://github.com/login/oauth/access_token',
[],
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://github.com/login/oauth/access_token',
[],
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
])
);
), true);
$output = [];
\parse_str($accessToken, $output);
if (isset($output['access_token'])) {
return $output['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -13,6 +13,11 @@ class Gitlab extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -46,28 +51,48 @@ class Gitlab extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
'POST',
'https://gitlab.com/oauth/token?'.\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
])
);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://gitlab.com/oauth/token?' . \http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
])
), true);
}
return '';
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://gitlab.com/oauth/token?' . \http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token'
])
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**

View file

@ -29,6 +29,11 @@ class Google extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -55,29 +60,49 @@ class Google extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
'POST',
'https://oauth2.googleapis.com/token?'.\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'scope' => null,
'grant_type' => 'authorization_code'
])
);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth2.googleapis.com/token?' . \http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'scope' => null,
'grant_type' => 'authorization_code'
])
), true);
}
return '';
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth2.googleapis.com/token?' . \http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token'
])
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**

View file

@ -10,6 +10,11 @@ class Linkedin extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -57,30 +62,53 @@ class Linkedin extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://www.linkedin.com/oauth/v2/accessToken',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://www.linkedin.com/oauth/v2/accessToken',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'redirect_uri' => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -14,6 +14,11 @@ class Microsoft extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -49,33 +54,55 @@ class Microsoft extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request(
'POST',
'https://login.microsoftonline.com/'.$this->getTenantId().'/oauth2/v2.0/token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
])
);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://login.microsoftonline.com/' . $this->getTenantId() . '/oauth2/v2.0/token',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
])
), true);
}
return '';
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://login.microsoftonline.com/' . $this->getTenantId() . '/oauth2/v2.0/token',
$headers,
\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'grant_type' => 'refresh_token'
])
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Mock extends OAuth2
{
@ -22,6 +23,11 @@ class Mock extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -47,28 +53,49 @@ class Mock extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
'GET',
'http://localhost/'.$this->version.'/mock/tests/general/oauth2/token?'.
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
])
);
$accessToken = \json_decode($accessToken, true); //
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'GET',
'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' .
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
])
), true);
}
return '';
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'GET',
'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' .
\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
])
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**

View file

@ -20,6 +20,11 @@ class Notion extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -51,32 +56,50 @@ class Notion extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$headers = [
"Authorization: Basic " . \base64_encode($this->appID . ":" . $this->appSecret),
];
if(empty($this->tokens)) {
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$headers,
\http_build_query([
'grant_type' => 'authorization_code',
'redirect_uri' => $this->callback,
'code' => $code
])
), true);
}
$response = $this->request(
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$headers,
\http_build_query([
'grant_type' => 'authorization_code',
'redirect_uri' => $this->callback,
'code' => $code
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
])
);
), true);
$response = \json_decode($response, true);
if (isset($response['access_token'])) {
return $response['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -34,6 +34,11 @@ class Paypal extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -74,29 +79,47 @@ class Paypal extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->resourceEndpoint[$this->environment] . 'oauth2/token',
['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)],
\http_build_query([
'code' => $code,
'grant_type' => 'authorization_code',
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->resourceEndpoint[$this->environment] . 'oauth2/token',
['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)],
\http_build_query([
'code' => $code,
'grant_type' => 'authorization_code',
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token',
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -15,6 +15,11 @@ class Salesforce extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -59,32 +64,56 @@ class Salesforce extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = [
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$this->tokens = \json_decode($this->request(
'POST',
'https://login.salesforce.com/services/oauth2/token',
$headers,
\http_build_query([
'code' => $code,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = [
"Authorization: Basic " . \base64_encode($this->appID . ":" . $this->appSecret),
"Content-Type: application/x-www-form-urlencoded",
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://login.salesforce.com/services/oauth2/token',
$headers,
\http_build_query([
'code' => $code,
'redirect_uri' => $this->callback ,
'grant_type' => 'authorization_code'
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
])
);
$accessToken = \json_decode($accessToken, true);
), true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Slack extends OAuth2
{
@ -10,6 +11,11 @@ class Slack extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -46,28 +52,48 @@ class Slack extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
// https://api.slack.com/docs/oauth#step_3_-_exchanging_a_verification_code_for_an_access_token
$accessToken = $this->request(
'GET',
'https://slack.com/api/oauth.access?'.\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'redirect_uri' => $this->callback
])
);
$accessToken = \json_decode($accessToken, true); //
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens)) {
// https://api.slack.com/docs/oauth#step_3_-_exchanging_a_verification_code_for_an_access_token
$this->tokens = \json_decode($this->request(
'GET',
'https://slack.com/api/oauth.access?' . \http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'redirect_uri' => $this->callback
])
), true);
}
return '';
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'GET',
'https://slack.com/api/oauth.access?' . \http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
])
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**

View file

@ -31,6 +31,11 @@ class Spotify extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -58,27 +63,50 @@ class Spotify extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$header = "Authorization: Basic " . \base64_encode($this->appID . ":" . $this->appSecret);
$result = \json_decode($this->request(
if(empty($this->tokens)) {
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'api/token',
$headers,
\http_build_query([
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'api/token',
[$header],
$headers,
\http_build_query([
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
"refresh_token" => $refreshToken,
"grant_type" => "refresh_token",
])
), true);
if (isset($result['access_token'])) {
return $result['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Stripe extends OAuth2
{
@ -10,6 +11,11 @@ class Stripe extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var string
@ -29,7 +35,7 @@ class Stripe extends OAuth2
protected $grantType = [
'authorize' => 'authorization_code',
'refresh' => 'refresh_token'
'refresh' => 'refresh_token',
];
/**
@ -57,31 +63,50 @@ class Stripe extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$response = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://connect.stripe.com/oauth/token',
[],
\http_build_query([
'grant_type' => $this->grantType['authorize'],
'code' => $code
])
), true);
$this->stripeAccountId = $this->tokens['stripe_user_id'];
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://connect.stripe.com/oauth/token',
[],
\http_build_query([
'grant_type' => $this->grantType['authorize'],
'code' => $code
'grant_type' => $this->grantType['refresh'],
'refresh_token' => $refreshToken,
])
);
), true);
$response = \json_decode($response, true);
if (isset($response['stripe_user_id'])) {
$this->stripeAccountId = $response['stripe_user_id'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
if (isset($response['access_token'])) {
return $response['access_token'];
}
return '';
$this->stripeAccountId = $this->tokens['stripe_user_id'];
return $this->tokens;
}
/**

View file

@ -33,6 +33,11 @@ class Tradeshift extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
protected $scopes = [
@ -69,23 +74,47 @@ class Tradeshift extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
$response = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint[$this->environment] . 'auth/token',
['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint[$this->environment] . 'auth/token',
['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
])
);
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
$accessToken = \json_decode($response, true);
return $accessToken['access_token'] ?? '';
return $this->tokens;
}
/**

View file

@ -31,6 +31,11 @@ class Twitch extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -59,26 +64,48 @@ class Twitch extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$result = \json_decode($this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'token?' . \http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'token?'. \http_build_query([
$this->endpoint . 'token?' . \http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
"refresh_token" => $refreshToken,
"grant_type" => "refresh_token",
])
), true);
if (isset($result['access_token'])) {
return $result['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -3,6 +3,7 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
// Reference Material
// https://vk.com/dev/first_guide
@ -16,6 +17,11 @@ class Vk extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -57,36 +63,59 @@ class Vk extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth.vk.com/access_token?',
$headers,
\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback
])
), true);
$this->user['email'] = $this->tokens['email'];
$this->user['user_id'] = $this->tokens['user_id'];
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth.vk.com/access_token?',
$headers,
\http_build_query([
'code' => $code,
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback
'grant_type' => 'refresh_token'
])
);
$accessToken = \json_decode($accessToken, true);
), true);
if (isset($accessToken['email'])) {
$this->user['email'] = $accessToken['email'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
if (isset($accessToken['user_id'])) {
$this->user['user_id'] = $accessToken['user_id'];
}
$this->user['email'] = $this->tokens['email'];
$this->user['user_id'] = $this->tokens['user_id'];
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
}
return '';
return $this->tokens;
}
/**

View file

@ -13,6 +13,11 @@ class WordPress extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -46,30 +51,52 @@ class WordPress extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$accessToken = $this->request(
if(empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://public-api.wordpress.com/oauth2/token',
[],
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'grant_type' => 'authorization_code',
'code' => $code
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://public-api.wordpress.com/oauth2/token',
[],
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'grant_type' => 'authorization_code',
'code' => $code
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -32,6 +32,11 @@ class Yahoo extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -70,31 +75,58 @@ class Yahoo extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code):string
protected function getTokens(string $code): array
{
$header = [
"Authorization: Basic " . \base64_encode($this->appID . ":" . $this->appSecret),
"Content-Type: application/x-www-form-urlencoded",
if(empty($this->tokens)) {
$headers = [
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'get_token',
$headers,
\http_build_query([
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = [
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$result = \json_decode($this->request(
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'get_token',
$header,
$headers,
\http_build_query([
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
"refresh_token" => $refreshToken,
"grant_type" => "refresh_token",
])
), true);
if (isset($result['access_token'])) {
return $result['access_token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -18,6 +18,11 @@ class Yammer extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @return string
@ -44,31 +49,53 @@ class Yammer extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'access_token?',
$headers,
\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'grant_type' => 'authorization_code'
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'access_token?',
$headers,
\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'grant_type' => 'authorization_code'
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
])
);
), true);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token']['token'])) {
return $accessToken['access_token']['token'];
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -15,6 +15,11 @@ class Yandex extends OAuth2
* @var array
*/
protected $user = [];
/**
* @var array
*/
protected $tokens = [];
/**
* @var array
@ -56,31 +61,55 @@ class Yandex extends OAuth2
/**
* @param string $code
*
* @return string
* @return array
*/
public function getAccessToken(string $code): string
protected function getTokens(string $code): array
{
if(empty($this->tokens)) {
$headers = [
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth.yandex.com/token',
$headers,
\http_build_query([
'code' => $code,
'grant_type' => 'authorization_code'
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken):array
{
$headers = [
"Authorization: Basic " . \base64_encode($this->appID . ":" . $this->appSecret),
"Content-Type: application/x-www-form-urlencoded",
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$accessToken = $this->request(
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth.yandex.com/token',
$headers,
\http_build_query([
'code' => $code,
'refresh_token' => $refreshToken,
'grant_type' => 'authorization_code'
])
);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) {
return $accessToken['access_token'];
), true);
if(empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return '';
return $this->tokens;
}
/**

View file

@ -40,9 +40,21 @@ class Session extends Model
'default' => '',
'example' => 'user@example.com',
])
->addRule('providerToken', [
->addRule('providerAccessToken', [
'type' => self::TYPE_STRING,
'description' => 'Session Provider Token.',
'description' => 'Session Provider Access Token.',
'default' => '',
'example' => 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3',
])
->addRule('providerAccessTokenExpiry', [
'type' => self::TYPE_INTEGER,
'description' => 'Date, the Unix timestamp of when the access token expires.',
'default' => 0,
'example' => 1592981250,
])
->addRule('providerRefreshToken', [
'type' => self::TYPE_STRING,
'description' => 'Session Provider Refresh Token.',
'default' => '',
'example' => 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3',
])

View file

@ -107,6 +107,7 @@ trait ProjectCustom
'account.verification.update',
'account.delete',
'account.sessions.create',
'account.sessions.update',
'account.sessions.delete',
'database.collections.create',
'database.collections.update',

View file

@ -3,7 +3,6 @@
namespace Tests\E2E\Services\Account;
use Tests\E2E\Client;
use function array_merge;
trait AccountBase
{

View file

@ -6,7 +6,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SideClient;
use Utopia\App;
use function sleep;
class AccountCustomClientTest extends Scope
{
@ -423,7 +423,7 @@ class AccountCustomClientTest extends Scope
'success' => 'http://localhost/v1/mock/tests/general/oauth2/success',
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
]);
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
$this->assertEquals(200, $response['headers']['status-code']);
@ -439,7 +439,39 @@ class AccountCustomClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertEquals($response['body']['$id'], $userId);
$this->assertEquals($response['body']['name'], 'User Name');
$this->assertEquals($response['body']['email'], 'user@localhost.test');
$this->assertEquals($response['body']['email'], 'useroauth@localhost.test');
// Since we only support one oauth user, let's also check updateSession here
$this->assertEquals($response['headers']['status-code'], 200);
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/current', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('123456', $response['body']['providerAccessToken']);
$this->assertEquals('tuvwxyz', $response['body']['providerRefreshToken']);
$this->assertGreaterThan(\time() + 14400 - 5, $response['body']['providerAccessTokenExpiry']); // 5 seconds allowed networking delay
$initialExpiry = $response['body']['providerAccessTokenExpiry'];
sleep(3);
$response = $this->client->call(Client::METHOD_PATCH, '/account/sessions/current', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('123456', $response['body']['providerAccessToken']);
$this->assertEquals('tuvwxyz', $response['body']['providerRefreshToken']);
$this->assertNotEquals($initialExpiry, $response['body']['providerAccessTokenExpiry']);
return [];
}