mirror of
https://github.com/elyby/accounts.git
synced 2024-11-30 02:32:26 +05:30
Протестирована логика подписи access_token и refresh_token, добавлены базовые скоупы, подчищен проект
This commit is contained in:
parent
f5f93ddef1
commit
1c6ba30abf
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @link http://www.yiiframework.com/
|
|
||||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
|
||||||
* @license http://www.yiiframework.com/license/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace api\assets;
|
|
||||||
|
|
||||||
use yii\web\AssetBundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
|
||||||
* @since 2.0
|
|
||||||
*/
|
|
||||||
class AppAsset extends AssetBundle
|
|
||||||
{
|
|
||||||
public $basePath = '@webroot';
|
|
||||||
public $baseUrl = '@web';
|
|
||||||
public $css = [
|
|
||||||
'css/site.css',
|
|
||||||
];
|
|
||||||
public $js = [
|
|
||||||
];
|
|
||||||
public $depends = [
|
|
||||||
'yii\web\YiiAsset',
|
|
||||||
'yii\bootstrap\BootstrapAsset',
|
|
||||||
];
|
|
||||||
}
|
|
@ -4,7 +4,9 @@ namespace api\controllers;
|
|||||||
use common\components\oauth\Exception\AcceptRequiredException;
|
use common\components\oauth\Exception\AcceptRequiredException;
|
||||||
use common\components\oauth\Exception\AccessDeniedException;
|
use common\components\oauth\Exception\AccessDeniedException;
|
||||||
use common\models\OauthClient;
|
use common\models\OauthClient;
|
||||||
|
use common\models\OauthScope;
|
||||||
use League\OAuth2\Server\Exception\OAuthException;
|
use League\OAuth2\Server\Exception\OAuthException;
|
||||||
|
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||||
use Yii;
|
use Yii;
|
||||||
use yii\filters\AccessControl;
|
use yii\filters\AccessControl;
|
||||||
use yii\helpers\ArrayHelper;
|
use yii\helpers\ArrayHelper;
|
||||||
@ -17,7 +19,7 @@ class OauthController extends Controller {
|
|||||||
'class' => AccessControl::class,
|
'class' => AccessControl::class,
|
||||||
'rules' => [
|
'rules' => [
|
||||||
[
|
[
|
||||||
'actions' => ['validate'],
|
'actions' => ['validate', 'issue-token'],
|
||||||
'allow' => true,
|
'allow' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -32,8 +34,9 @@ class OauthController extends Controller {
|
|||||||
|
|
||||||
public function verbs() {
|
public function verbs() {
|
||||||
return [
|
return [
|
||||||
'validate' => ['GET'],
|
'validate' => ['GET'],
|
||||||
'complete' => ['POST'],
|
'complete' => ['POST'],
|
||||||
|
'issue-token' => ['POST'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,20 +144,28 @@ class OauthController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Метод выполняется сервером приложения, которому был выдан auth_token.
|
* Метод выполняется сервером приложения, которому был выдан auth_token иди refresh_token.
|
||||||
*
|
*
|
||||||
* Входными данными является стандартный список GET параметров по стандарту oAuth:
|
* Входными данными является стандартный список GET параметров по стандарту oAuth:
|
||||||
* $_GET = [
|
* $_GET = [
|
||||||
* client_id,
|
* client_id,
|
||||||
* client_secret,
|
* client_secret,
|
||||||
* redirect_uri,
|
* redirect_uri,
|
||||||
* code|refresh_token,
|
* code,
|
||||||
|
* grant_type,
|
||||||
|
* ]
|
||||||
|
* для запроса grant_type = authentication_code.
|
||||||
|
* $_GET = [
|
||||||
|
* client_id,
|
||||||
|
* client_secret,
|
||||||
|
* refresh_token,
|
||||||
* grant_type,
|
* grant_type,
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function actionIssueToken() {
|
public function actionIssueToken() {
|
||||||
|
$this->attachRefreshTokenGrantIfNeedle();
|
||||||
try {
|
try {
|
||||||
$response = $this->getServer()->issueAccessToken();
|
$response = $this->getServer()->issueAccessToken();
|
||||||
} catch (OAuthException $e) {
|
} catch (OAuthException $e) {
|
||||||
@ -168,6 +179,28 @@ class OauthController extends Controller {
|
|||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function attachRefreshTokenGrantIfNeedle() {
|
||||||
|
$grantType = Yii::$app->request->post('grant_type');
|
||||||
|
if ($grantType === 'authorization_code' && Yii::$app->request->post('code')) {
|
||||||
|
$authCode = Yii::$app->request->post('code');
|
||||||
|
$codeModel = $this->getServer()->getAuthCodeStorage()->get($authCode);
|
||||||
|
if ($codeModel === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scopes = $codeModel->getScopes();
|
||||||
|
if (array_search(OauthScope::OFFLINE_ACCESS, array_keys($scopes)) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} elseif ($grantType === 'refresh_token') {
|
||||||
|
// Это валидный кейс
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getServer()->addGrantType(new RefreshTokenGrant());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $queryParams
|
* @param array $queryParams
|
||||||
* @param OauthClient $clientModel
|
* @param OauthClient $clientModel
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this \yii\web\View */
|
|
||||||
/* @var $content string */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
use yii\bootstrap\Nav;
|
|
||||||
use yii\bootstrap\NavBar;
|
|
||||||
use yii\widgets\Breadcrumbs;
|
|
||||||
use api\assets\AppAsset;
|
|
||||||
|
|
||||||
AppAsset::register($this);
|
|
||||||
?>
|
|
||||||
<?php $this->beginPage() ?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="<?= Yii::$app->language ?>">
|
|
||||||
<head>
|
|
||||||
<meta charset="<?= Yii::$app->charset ?>">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<?= Html::csrfMetaTags() ?>
|
|
||||||
<title><?= Html::encode($this->title) ?></title>
|
|
||||||
<?php $this->head() ?>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<?php $this->beginBody() ?>
|
|
||||||
|
|
||||||
<div class="wrap">
|
|
||||||
<?php
|
|
||||||
NavBar::begin([
|
|
||||||
'brandLabel' => 'My Company',
|
|
||||||
'brandUrl' => Yii::$app->homeUrl,
|
|
||||||
'options' => [
|
|
||||||
'class' => 'navbar-inverse navbar-fixed-top',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
$menuItems = [
|
|
||||||
['label' => 'Home', 'url' => ['site/index']],
|
|
||||||
['label' => 'About', 'url' => ['site/about']],
|
|
||||||
['label' => 'Contact', 'url' => ['site/contact']],
|
|
||||||
];
|
|
||||||
if (Yii::$app->user->isGuest) {
|
|
||||||
$menuItems[] = ['label' => 'Signup', 'url' => ['site/signup']];
|
|
||||||
$menuItems[] = ['label' => 'Login', 'url' => ['site/login']];
|
|
||||||
} else {
|
|
||||||
$menuItems[] = [
|
|
||||||
'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
|
|
||||||
'url' => ['site/logout'],
|
|
||||||
'linkOptions' => ['data-method' => 'post']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
echo Nav::widget([
|
|
||||||
'options' => ['class' => 'navbar-nav navbar-right'],
|
|
||||||
'items' => $menuItems,
|
|
||||||
]);
|
|
||||||
NavBar::end();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<?= Breadcrumbs::widget([
|
|
||||||
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
|
|
||||||
]) ?>
|
|
||||||
<?= $content ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="container">
|
|
||||||
<p class="pull-left">© My Company <?= date('Y') ?></p>
|
|
||||||
|
|
||||||
<p class="pull-right"><?= Yii::powered() ?></p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<?php $this->endBody() ?>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
<?php $this->endPage() ?>
|
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
|
|
||||||
$this->title = 'About';
|
|
||||||
$this->params['breadcrumbs'][] = $this->title;
|
|
||||||
?>
|
|
||||||
<div class="site-about">
|
|
||||||
<h1><?= Html::encode($this->title) ?></h1>
|
|
||||||
|
|
||||||
<p>This is the About page. You may modify the following file to customize its content:</p>
|
|
||||||
|
|
||||||
<code><?= __FILE__ ?></code>
|
|
||||||
</div>
|
|
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
/* @var $form yii\bootstrap\ActiveForm */
|
|
||||||
/* @var $model \api\models\ContactForm */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
use yii\bootstrap\ActiveForm;
|
|
||||||
use yii\captcha\Captcha;
|
|
||||||
|
|
||||||
$this->title = 'Contact';
|
|
||||||
$this->params['breadcrumbs'][] = $this->title;
|
|
||||||
?>
|
|
||||||
<div class="site-contact">
|
|
||||||
<h1><?= Html::encode($this->title) ?></h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-5">
|
|
||||||
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'name') ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'email') ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'subject') ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'body')->textArea(['rows' => 6]) ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
|
|
||||||
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
|
|
||||||
]) ?>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php ActiveForm::end(); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
/* @var $name string */
|
|
||||||
/* @var $message string */
|
|
||||||
/* @var $exception Exception */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
|
|
||||||
$this->title = $name;
|
|
||||||
?>
|
|
||||||
<div class="site-error">
|
|
||||||
|
|
||||||
<h1><?= Html::encode($this->title) ?></h1>
|
|
||||||
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<?= nl2br(Html::encode($message)) ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The above error occurred while the Web server was processing your request.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Please contact us if you think this is a server error. Thank you.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
|
|
||||||
$this->title = 'My Yii Application';
|
|
||||||
?>
|
|
||||||
<div class="site-index">
|
|
||||||
|
|
||||||
<div class="jumbotron">
|
|
||||||
<h1>Congratulations!</h1>
|
|
||||||
|
|
||||||
<p class="lead">You have successfully created your Yii-powered application.</p>
|
|
||||||
|
|
||||||
<p><a class="btn btn-lg btn-success" href="http://www.yiiframework.com">Get started with Yii</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="body-content">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<h2>Heading</h2>
|
|
||||||
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
|
||||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
|
||||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
|
||||||
fugiat nulla pariatur.</p>
|
|
||||||
|
|
||||||
<p><a class="btn btn-default" href="http://www.yiiframework.com/doc/">Yii Documentation »</a></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<h2>Heading</h2>
|
|
||||||
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
|
||||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
|
||||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
|
||||||
fugiat nulla pariatur.</p>
|
|
||||||
|
|
||||||
<p><a class="btn btn-default" href="http://www.yiiframework.com/forum/">Yii Forum »</a></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<h2>Heading</h2>
|
|
||||||
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
|
||||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
|
||||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
|
||||||
fugiat nulla pariatur.</p>
|
|
||||||
|
|
||||||
<p><a class="btn btn-default" href="http://www.yiiframework.com/extensions/">Yii Extensions »</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
/* @var $form yii\bootstrap\ActiveForm */
|
|
||||||
/* @var $model \api\models\PasswordResetRequestForm */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
use yii\bootstrap\ActiveForm;
|
|
||||||
|
|
||||||
$this->title = 'Request password reset';
|
|
||||||
$this->params['breadcrumbs'][] = $this->title;
|
|
||||||
?>
|
|
||||||
<div class="site-request-password-reset">
|
|
||||||
<h1><?= Html::encode($this->title) ?></h1>
|
|
||||||
|
|
||||||
<p>Please fill out your email. A link to reset password will be sent there.</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-5">
|
|
||||||
<?php $form = ActiveForm::begin(['id' => 'request-password-reset-form']); ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'email') ?>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<?= Html::submitButton('Send', ['class' => 'btn btn-primary']) ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php ActiveForm::end(); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
/* @var $form yii\bootstrap\ActiveForm */
|
|
||||||
/* @var $model \api\models\ResetPasswordForm */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
use yii\bootstrap\ActiveForm;
|
|
||||||
|
|
||||||
$this->title = 'Reset password';
|
|
||||||
$this->params['breadcrumbs'][] = $this->title;
|
|
||||||
?>
|
|
||||||
<div class="site-reset-password">
|
|
||||||
<h1><?= Html::encode($this->title) ?></h1>
|
|
||||||
|
|
||||||
<p>Please choose your new password:</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-5">
|
|
||||||
<?php $form = ActiveForm::begin(['id' => 'reset-password-form']); ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'password')->passwordInput() ?>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php ActiveForm::end(); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* @var $this yii\web\View */
|
|
||||||
/* @var $form yii\bootstrap\ActiveForm */
|
|
||||||
/* @var $model \api\models\SignupForm */
|
|
||||||
|
|
||||||
use yii\helpers\Html;
|
|
||||||
use yii\bootstrap\ActiveForm;
|
|
||||||
|
|
||||||
$this->title = 'Signup';
|
|
||||||
$this->params['breadcrumbs'][] = $this->title;
|
|
||||||
?>
|
|
||||||
<div class="site-signup">
|
|
||||||
<h1><?= Html::encode($this->title) ?></h1>
|
|
||||||
|
|
||||||
<p>Please fill out the following fields to signup:</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-5">
|
|
||||||
<?php $form = ActiveForm::begin(['id' => 'form-signup']); ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'username') ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'email') ?>
|
|
||||||
|
|
||||||
<?= $form->field($model, 'password')->passwordInput() ?>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php ActiveForm::end(); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -2,6 +2,7 @@
|
|||||||
namespace common\components\oauth;
|
namespace common\components\oauth;
|
||||||
|
|
||||||
use common\components\oauth\Storage\Redis\AuthCodeStorage;
|
use common\components\oauth\Storage\Redis\AuthCodeStorage;
|
||||||
|
use common\components\oauth\Storage\Redis\RefreshTokenStorage;
|
||||||
use common\components\oauth\Storage\Yii2\AccessTokenStorage;
|
use common\components\oauth\Storage\Yii2\AccessTokenStorage;
|
||||||
use common\components\oauth\Storage\Yii2\ClientStorage;
|
use common\components\oauth\Storage\Yii2\ClientStorage;
|
||||||
use common\components\oauth\Storage\Yii2\ScopeStorage;
|
use common\components\oauth\Storage\Yii2\ScopeStorage;
|
||||||
@ -43,6 +44,7 @@ class Component extends \yii\base\Component {
|
|||||||
->setScopeStorage(new ScopeStorage())
|
->setScopeStorage(new ScopeStorage())
|
||||||
->setSessionStorage(new SessionStorage())
|
->setSessionStorage(new SessionStorage())
|
||||||
->setAuthCodeStorage(new AuthCodeStorage())
|
->setAuthCodeStorage(new AuthCodeStorage())
|
||||||
|
->setRefreshTokenStorage(new RefreshTokenStorage())
|
||||||
->setScopeDelimiter(',');
|
->setScopeDelimiter(',');
|
||||||
|
|
||||||
$this->_authServer = $authServer;
|
$this->_authServer = $authServer;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
namespace common\components\oauth\Entity;
|
namespace common\components\oauth\Entity;
|
||||||
|
|
||||||
use League\OAuth2\Server\Entity\EntityTrait;
|
use League\OAuth2\Server\Entity\EntityTrait;
|
||||||
use League\OAuth2\Server\Entity\SessionEntity;
|
use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity;
|
||||||
|
|
||||||
class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity {
|
class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity {
|
||||||
use EntityTrait;
|
use EntityTrait;
|
||||||
@ -17,7 +17,7 @@ class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity {
|
|||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public function setSession(SessionEntity $session) {
|
public function setSession(OriginalSessionEntity $session) {
|
||||||
parent::setSession($session);
|
parent::setSession($session);
|
||||||
$this->sessionId = $session->getId();
|
$this->sessionId = $session->getId();
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class AuthCodeStorage extends AbstractStorage implements AuthCodeInterface {
|
|||||||
'id' => $result['id'],
|
'id' => $result['id'],
|
||||||
'redirectUri' => $result['client_redirect_uri'],
|
'redirectUri' => $result['client_redirect_uri'],
|
||||||
'expireTime' => $result['expire_time'],
|
'expireTime' => $result['expire_time'],
|
||||||
'sessionId' => $result['sessionId'],
|
'sessionId' => $result['session_id'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Fahmiardi\OAuth2\Server\Storage\Redis;
|
namespace common\components\oauth\Storage\Redis;
|
||||||
|
|
||||||
use common\components\redis\Key;
|
use common\components\redis\Key;
|
||||||
use League\OAuth2\Server\Entity\RefreshTokenEntity;
|
use League\OAuth2\Server\Entity\RefreshTokenEntity;
|
||||||
|
@ -18,7 +18,7 @@ class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface
|
|||||||
* @return OauthAccessToken|null
|
* @return OauthAccessToken|null
|
||||||
*/
|
*/
|
||||||
private function getTokenModel($token) {
|
private function getTokenModel($token) {
|
||||||
if (isset($this->cache[$token])) {
|
if (!isset($this->cache[$token])) {
|
||||||
$this->cache[$token] = OauthAccessToken::findOne($token);
|
$this->cache[$token] = OauthAccessToken::findOne($token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class Key {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getValue() {
|
public function getValue() {
|
||||||
return $this->getRedis()->get(json_decode($this->key));
|
return json_decode($this->getRedis()->get($this->key), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setValue($value) {
|
public function setValue($value) {
|
||||||
|
@ -10,6 +10,9 @@ use yii\db\ActiveRecord;
|
|||||||
*/
|
*/
|
||||||
class OauthScope extends ActiveRecord {
|
class OauthScope extends ActiveRecord {
|
||||||
|
|
||||||
|
const OFFLINE_ACCESS = 'offline_access';
|
||||||
|
const MINECRAFT_SERVER_SESSION = 'minecraft_server_session';
|
||||||
|
|
||||||
public static function tableName() {
|
public static function tableName() {
|
||||||
return '{{%oauth_scopes}}';
|
return '{{%oauth_scopes}}';
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ class OauthSession extends ActiveRecord {
|
|||||||
return '{{%oauth_sessions}}';
|
return '{{%oauth_sessions}}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOauthAccessTokens() {
|
public function getAccessTokens() {
|
||||||
return $this->hasMany(OauthAccessToken::class, ['session_id' => 'id']);
|
return $this->hasMany(OauthAccessToken::class, ['session_id' => 'id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
console/migrations/m160222_204006_add_init_scopes.php
Normal file
18
console/migrations/m160222_204006_add_init_scopes.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use console\db\Migration;
|
||||||
|
|
||||||
|
class m160222_204006_add_init_scopes extends Migration {
|
||||||
|
|
||||||
|
public function safeUp() {
|
||||||
|
$this->batchInsert('{{%oauth_scopes}}', ['id'], [
|
||||||
|
['offline_access'],
|
||||||
|
['minecraft_server_session'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function safeDown() {
|
||||||
|
$this->delete('{{%oauth_scopes}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,4 +18,9 @@ class OauthRoute extends BasePage {
|
|||||||
$this->actor->sendPOST($this->getUrl($queryParams), $postParams);
|
$this->actor->sendPOST($this->getUrl($queryParams), $postParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function issueToken($postParams = []) {
|
||||||
|
$this->route = ['oauth/issue-token'];
|
||||||
|
$this->actor->sendPOST($this->getUrl(), $postParams);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
99
tests/codeception/api/functional/OauthAccessTokenCest.php
Normal file
99
tests/codeception/api/functional/OauthAccessTokenCest.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
namespace tests\codeception\api;
|
||||||
|
|
||||||
|
use Codeception\Scenario;
|
||||||
|
use tests\codeception\api\_pages\OauthRoute;
|
||||||
|
use tests\codeception\api\functional\_steps\OauthSteps;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
class OauthAccessTokenCest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OauthRoute
|
||||||
|
*/
|
||||||
|
private $route;
|
||||||
|
|
||||||
|
public function _before(FunctionalTester $I) {
|
||||||
|
$this->route = new OauthRoute($I);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIssueTokenWithWrongArgs(FunctionalTester $I) {
|
||||||
|
$I->wantTo('check behavior on on request without any credentials');
|
||||||
|
$this->route->issueToken();
|
||||||
|
$I->canSeeResponseCodeIs(400);
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'error' => 'invalid_request',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$I->wantTo('check behavior on passing invalid auth code');
|
||||||
|
$this->route->issueToken($this->buildParams(
|
||||||
|
'wrong-auth-code',
|
||||||
|
'ely',
|
||||||
|
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||||
|
'http://ely.by'
|
||||||
|
));
|
||||||
|
$I->canSeeResponseCodeIs(400);
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'error' => 'invalid_request',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIssueToken(FunctionalTester $I, Scenario $scenario) {
|
||||||
|
$I = new OauthSteps($scenario);
|
||||||
|
$authCode = $I->getAuthCode();
|
||||||
|
$this->route->issueToken($this->buildParams(
|
||||||
|
$authCode,
|
||||||
|
'ely',
|
||||||
|
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||||
|
'http://ely.by'
|
||||||
|
));
|
||||||
|
$I->canSeeResponseCodeIs(200);
|
||||||
|
$I->canSeeResponseIsJson();
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'token_type' => 'Bearer',
|
||||||
|
]);
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIssueTokenWithRefreshToken(FunctionalTester $I, Scenario $scenario) {
|
||||||
|
$I = new OauthSteps($scenario);
|
||||||
|
$authCode = $I->getAuthCode(false);
|
||||||
|
$this->route->issueToken($this->buildParams(
|
||||||
|
$authCode,
|
||||||
|
'ely',
|
||||||
|
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||||
|
'http://ely.by'
|
||||||
|
));
|
||||||
|
$I->canSeeResponseCodeIs(200);
|
||||||
|
$I->canSeeResponseIsJson();
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'token_type' => 'Bearer',
|
||||||
|
]);
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildParams($code = null, $clientId = null, $clientSecret = null, $redirectUri = null) {
|
||||||
|
$params = ['grant_type' => 'authorization_code'];
|
||||||
|
if ($code !== null) {
|
||||||
|
$params['code'] = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clientId !== null) {
|
||||||
|
$params['client_id'] = $clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clientSecret !== null) {
|
||||||
|
$params['client_secret'] = $clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($redirectUri !== null) {
|
||||||
|
$params['redirect_uri'] = $redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,8 +3,9 @@ namespace tests\codeception\api;
|
|||||||
|
|
||||||
use tests\codeception\api\_pages\OauthRoute;
|
use tests\codeception\api\_pages\OauthRoute;
|
||||||
use tests\codeception\api\functional\_steps\AccountSteps;
|
use tests\codeception\api\functional\_steps\AccountSteps;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
class OauthCest {
|
class OauthAuthCodeCest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var OauthRoute
|
* @var OauthRoute
|
99
tests/codeception/api/functional/OauthRefreshTokenCest.php
Normal file
99
tests/codeception/api/functional/OauthRefreshTokenCest.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
namespace tests\codeception\api;
|
||||||
|
|
||||||
|
use Codeception\Scenario;
|
||||||
|
use common\models\OauthScope;
|
||||||
|
use tests\codeception\api\_pages\OauthRoute;
|
||||||
|
use tests\codeception\api\functional\_steps\OauthSteps;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
class OauthRefreshTokenCest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OauthRoute
|
||||||
|
*/
|
||||||
|
private $route;
|
||||||
|
|
||||||
|
public function _before(FunctionalTester $I) {
|
||||||
|
$this->route = new OauthRoute($I);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRefreshToken(FunctionalTester $I, Scenario $scenario) {
|
||||||
|
$I = new OauthSteps($scenario);
|
||||||
|
$refreshToken = $I->getRefreshToken();
|
||||||
|
$this->route->issueToken($this->buildParams(
|
||||||
|
$refreshToken,
|
||||||
|
'ely',
|
||||||
|
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM'
|
||||||
|
));
|
||||||
|
$I->canSeeResponseCodeIs(200);
|
||||||
|
$I->canSeeResponseIsJson();
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'token_type' => 'Bearer',
|
||||||
|
]);
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRefreshTokenWithSameScopes(FunctionalTester $I, Scenario $scenario) {
|
||||||
|
$I = new OauthSteps($scenario);
|
||||||
|
$refreshToken = $I->getRefreshToken();
|
||||||
|
$this->route->issueToken($this->buildParams(
|
||||||
|
$refreshToken,
|
||||||
|
'ely',
|
||||||
|
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||||
|
[OauthScope::MINECRAFT_SERVER_SESSION, OauthScope::OFFLINE_ACCESS]
|
||||||
|
));
|
||||||
|
$I->canSeeResponseCodeIs(200);
|
||||||
|
$I->canSeeResponseIsJson();
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'token_type' => 'Bearer',
|
||||||
|
]);
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||||
|
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRefreshTokenWithNewScopes(FunctionalTester $I, Scenario $scenario) {
|
||||||
|
$I = new OauthSteps($scenario);
|
||||||
|
$refreshToken = $I->getRefreshToken();
|
||||||
|
$this->route->issueToken($this->buildParams(
|
||||||
|
$refreshToken,
|
||||||
|
'ely',
|
||||||
|
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||||
|
[OauthScope::MINECRAFT_SERVER_SESSION, OauthScope::OFFLINE_ACCESS, 'change_skin']
|
||||||
|
));
|
||||||
|
$I->canSeeResponseCodeIs(400);
|
||||||
|
$I->canSeeResponseIsJson();
|
||||||
|
$I->canSeeResponseContainsJson([
|
||||||
|
'error' => 'invalid_scope',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildParams($refreshToken = null, $clientId = null, $clientSecret = null, $scopes = []) {
|
||||||
|
$params = ['grant_type' => 'refresh_token'];
|
||||||
|
if ($refreshToken !== null) {
|
||||||
|
$params['refresh_token'] = $refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clientId !== null) {
|
||||||
|
$params['client_id'] = $clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clientSecret !== null) {
|
||||||
|
$params['client_secret'] = $clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($scopes)) {
|
||||||
|
if (is_array($scopes)) {
|
||||||
|
$scopes = implode(',', $scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$params['scope'] = $scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
tests/codeception/api/functional/_steps/OauthSteps.php
Normal file
42
tests/codeception/api/functional/_steps/OauthSteps.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
namespace tests\codeception\api\functional\_steps;
|
||||||
|
|
||||||
|
use tests\codeception\api\_pages\OauthRoute;
|
||||||
|
|
||||||
|
class OauthSteps extends AccountSteps {
|
||||||
|
|
||||||
|
public function getAuthCode($online = true) {
|
||||||
|
// TODO: по идее можно напрямую сделать зпись в базу, что ускорит процесс тестирования
|
||||||
|
$this->loggedInAsActiveAccount();
|
||||||
|
$route = new OauthRoute($this);
|
||||||
|
$route->complete([
|
||||||
|
'client_id' => 'ely',
|
||||||
|
'redirect_uri' => 'http://ely.by',
|
||||||
|
'response_type' => 'code',
|
||||||
|
'scope' => 'minecraft_server_session' . ($online ? '' : ',offline_access'),
|
||||||
|
], ['accept' => true]);
|
||||||
|
$this->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||||
|
$response = json_decode($this->grabResponse(), true);
|
||||||
|
preg_match('/code=(\w+)/', $response['redirectUri'], $matches);
|
||||||
|
|
||||||
|
return $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRefreshToken() {
|
||||||
|
// TODO: по идее можно напрямую сделать зпись в базу, что ускорит процесс тестирования
|
||||||
|
$authCode = $this->getAuthCode(false);
|
||||||
|
$route = new OauthRoute($this);
|
||||||
|
$route->issueToken([
|
||||||
|
'code' => $authCode,
|
||||||
|
'client_id' => 'ely',
|
||||||
|
'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||||
|
'redirect_uri' => 'http://ely.by',
|
||||||
|
'grant_type' => 'authorization_code',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = json_decode($this->grabResponse(), true);
|
||||||
|
|
||||||
|
return $response['refresh_token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,4 +6,7 @@ return [
|
|||||||
'change_skin' => [
|
'change_skin' => [
|
||||||
'id' => 'change_skin',
|
'id' => 'change_skin',
|
||||||
],
|
],
|
||||||
|
'offline_access' => [
|
||||||
|
'id' => 'offline_access',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user