1
0
Fork 0
mirror of synced 2024-06-29 11:40:45 +12:00

Merge branch 'master' of https://github.com/appwrite/appwrite into feat-permissions-improvements

This commit is contained in:
Torsten Dittmann 2022-02-07 13:25:23 +01:00
commit 5cb3c2b7e3
53 changed files with 1502 additions and 469 deletions

View file

@ -1,9 +1,10 @@
name: "Tests"
on: [pull_request]
jobs:
tests:
name: Unit & E2E
runs-on: ubuntu-latest
runs-on: self-hosted
steps:
- name: Checkout repository
@ -18,29 +19,16 @@ jobs:
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
- name: Prepare Docker
- name: Build Appwrite
# Upstream bug causes buildkit pulls to fail so prefetch base images
# https://github.com/moby/moby/issues/41864
run: |
export COMPOSE_INTERACTIVE_NO_CLI
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env
docker pull composer:2.0
docker pull php:8.0-cli-alpine
docker compose pull
- name: Prepare Cache
uses: satackey/action-docker-layer-caching@v0.0.11
# Ignore the failure of a step and avoid terminating the job.
continue-on-error: true
- name: Build Appwrite
run: docker compose build --progress=plain
- name: Start Appwrite
run: |
docker compose build --progress=plain
docker compose up -d
sleep 30
sleep 10
- name: Doctor
run: docker compose exec -T appwrite doctor
@ -49,3 +37,9 @@ jobs:
- name: Run Tests
run: docker compose exec -T appwrite test --debug
- name: Teardown
if: always()
run: |
docker compose down -v
docker ps -aq | xargs docker rm --force

View file

@ -12,7 +12,7 @@
<!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) -->
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
[![Docker Pulls](https://img.shields.io/docker/pulls/appwrite/appwrite?color=f02e65&style=flat-square)](https://hub.docker.com/r/appwrite/appwrite)
[![Build Status](https://img.shields.io/github/checks-status/appwrite/appwrite/master?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions)
[![Build Status](https://img.shields.io/github/workflow/status/appwrite/appwrite/Tests?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions)
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![Translate](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md)
[![Swag Store](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io)

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,

View file

@ -116,7 +116,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '0.3.1',
'version' => '0.3.3',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@ -370,7 +370,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '0.2.2',
'version' => '0.2.5',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,

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,11 +479,13 @@ 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', 'authorization_code', 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 ($client_id != '1') {
@ -493,11 +496,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 +536,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

@ -137,7 +137,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
</div>
</div>
<p class="margin-top-small"><i class="icon-info-circled"></i> Data is aggregated and updated every 15 minutes</p>
<p class="margin-top-small text-size-small"><i class="icon-info-circled"></i> Data is aggregated and updated every 15 minutes</p>
</div>
</div>
</div>

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,32 +1,31 @@
// .loader {
// height: 0;
// direction: ltr;
// position: fixed;
// top: 0;
// width: 0;
// margin: 0 auto;
// .func-start(0);
// opacity: 0;
// z-index: 4;
// }
.loader {
height: 0;
direction: ltr;
position: fixed;
top: 0;
width: 0;
margin: 0 auto;
.func-start(0);
opacity: 0;
z-index: 4;
}
// .load-start ~ .loader {
// width: 100%;
// background: var(--config-color-focus);
// height: 3px;
// opacity: 1;
// transition: width .3s ease-out, opacity .5s ease-out;
// -moz-transition: width .3s ease-out, opacity .5s ease-out;
// -webkit-transition: width .3s ease-out, opacity .5s ease-out;
// -o-transition: width .3s ease-out, opacity .5s ease-out;
// }
.load-start ~ .loader {
width: 100%;
background: var(--config-color-focus);
height: 3px;
opacity: 1;
transition: width .3s ease-out, opacity .5s ease-out;
-moz-transition: width .3s ease-out, opacity .5s ease-out;
-webkit-transition: width .3s ease-out, opacity .5s ease-out;
-o-transition: width .3s ease-out, opacity .5s ease-out;
}
// .www .load-start ~ .loader {
// background: #fff;
// }
.www .load-start ~ .loader {
background: #fff;
}
/*
.load-start > * {
opacity: 0;
}
@ -37,7 +36,7 @@
-moz-transition: opacity .3s ease-out;
-webkit-transition: opacity .3s ease-out;
-o-transition: opacity .3s ease-out;
}*/
}
[data-ls-if] {
display: none;

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

@ -325,9 +325,14 @@ class OpenAPI3 extends Format
$node['schema']['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
case 'Utopia\Validator\Integer':
$node['schema']['type'] = $validator->getType();
$node['schema']['format'] = 'int32';
break;
case 'Utopia\Validator\FloatValidator':
$node['schema']['type'] = 'number';
$node['schema']['format'] = 'float';
break;
case 'Utopia\Validator\Length':
$node['schema']['type'] = $validator->getType();
break;

View file

@ -319,6 +319,10 @@ class Swagger2 extends Format
$node['type'] = $validator->getType();
$node['format'] = 'int32';
break;
case 'Utopia\Validator\FloatValidator':
$node['type'] = 'number';
$node['format'] = 'float';
break;
case 'Utopia\Validator\Length':
$node['type'] = $validator->getType();
break;

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 [];
}