Giter Site home page Giter Site logo

yii2-oauth2-rest-template's Introduction

REST API application with OAuth2 server on Yii2

This is a Yii2 Rest App template configured with OAuth2 server (using https://github.com/Filsh/yii2-oauth2-server). Resolved all problems on adaptation OAuth2 server extension, built directory structure with versions (as recommended in official guide), added some ready-to-use features for faster development.

You can use this template as a starting poing to create API side of your service.

Run on Docker

To quick run for testing code I've created this https://github.com/ikaras/yii2-oauth2-rest-docker - it using Docker Compose to up LEMP stack with all need configurations and ready for requests. Just follow instructions in there.

Installation

Install via Composer

If you do not have Composer, you may install it by following the instructions at getcomposer.org.

You can then install the application using the following commands:

composer global require "fxp/composer-asset-plugin:~1.1.1"
composer create-project --stability="dev" --prefer-source ikaras/yii2-oauth2-rest-template .

Configurations

  1. Configure your Web server i.e Nginx or Apache (see how here), to look at the application/api/www directory. I used domain api.loc in my tests.
  2. Change connection to your db in application/api/config/common.php
  3. Run migrations
php application/api/yiic migrate --migrationPath=@yii/rbac/migrations --interactive=0 \
php application/api/yiic migrate --migrationPath=@vendor/filsh/yii2-oauth2-server/migrations --interactive=0 \
php application/api/yiic migrate --interactive=0 \
php application/api/yiic migrate --interactive=0 \

Structure

\application              # root folder for environment (frontend, backend, api, etc.)
  \api                   # here is code for build REST API with OAuth2 server
  | \common              # common controllers and models for versions
  | | \controllers       # for tests created only one ProductController
  | | \models            # and one model Product
  | \components          # global for API components (parents for project's controllers, models, filters)
  | |-APIModule.php      # parent for all module/versions
  | |-ActiveController.php # child of yii's rest ActiveController, parent for all ones in project
  | |-Controller.php     # child of yii's rest Controller
  | | \db                # contain parents for all project's ActiveRecord and ActiveQuery
  | | \filters           
  | | | -OAuth2AccessFilter.php # MAIN IMPROVEMENT: analyze action publicity and scopes to attach filter
  | | |                          # to authorize by access token
  | | \traits            # contain ControllersCommonTrait.php with configuration of filters, used in both rest controllers
  | \config
  | \migrations          # contain migrations for create users table, filling demo products and scopes
  | \models              # have User model
  | \versions            # directory for versions, each version is module as recommend in official yii2 guide
  | | \v1                # created for test 1st version with childs of ProductController and Product model
  | \www                 # public directory, containt index.php

Tests

Conditions

  • Domain: api.loc (you can use also something like http://localhost/TestOAuth2/application/api/www/index.php/ if you don't want to use vhosts)
  • Version: v1
  • API point: /products, /products/<id>, /products/custom, /products/protected
  • User: login: [email protected], pass: 123123123
  • Scopes: default (default scope, CO :)), custom, protected - for accessing to /products/custom and /products/protected point

Description

For test have been created active rest controller ProductController for manage Product model.

This controller has the following access rules (in yii2 format):

	public function accessRules()
	{
		return [
			['allow' => true, 'roles' => ['?']],
			[
			  'allow' => true, 
			  'actions' => ['view','create','update','delete'],
				'roles' => ['@'],
			],
			[
				'allow' => true,
				'actions' => ['custom'],
				'roles' => ['@'],
				'scopes' => ['custom'],
			],
			[
				'allow' => true,
				'actions' => ['protected'],
				'roles' => ['@'],
				'scopes' => ['protected'],
			]
		];
	}

Each controller in the api can override method accessRules with access rules for itself. Rule can contain additional property - scope, this means access token should have addtional permissions.

So, from the rules, we understand that:

  • all actions of controller are opened (requests cab be without access token), but
  • actions view, create, update, delete - available only for authorized users
  • and for actions custom and protected needs additional scopes

Test requests

1. Request to public api points

    curl -i -H "Accept:application/json" -H "Content-Type:application/json" "http://api.loc/v1/products"

2. Request to get access token

curl -i -H "Accept:application/json" -H "Content-Type:application/json" "http://api.loc/oauth2/token" -XPOST \
-d '{"grant_type":"password","username":"[email protected]","password":"123123123","client_id":"testclient","client_secret":"testpass"}'

3. Request to get access token with scopes

curl -i -H "Accept:application/json" -H "Content-Type:application/json" "http://api.loc/oauth2/token" -XPOST \
-d '{"grant_type":"password","username":"[email protected]","password":"123123123","client_id":"testclient","client_secret":"testpass","scope":"custom"}'

4. Request to protected api point

curl -i -H "Accept:application/json" -H "Content-Type:application/json" \
"http://api.loc/v1/products/1?access_token=76f4c0d40347f24a73799335cefb495be9ea364b"

What have been done?

  1. Got Yii2 framework and created general directory structure (see section Structure).
  2. Configured Yii2 as RESTful Web Service using official manual.
  3. Created parent classes to inherit for all components of the project (saved in components directory). For more information - look at Structure section too. Classes Controller and ActiveController are parents for all controllers what use the same trait. ControllersCommonTrait - it connects (redefine) all needed filters to controller's actions.
  4. Directory common consists from the common controllers and models for each versions.
  5. Then I attached and configured Filsh's Yii2 OAuth2 extension, his extension based on widely used OAuth2 Server Library for PHP. All detailed information you can find in these repositories. What I've configured:
  1. Developed OAuth2AccessFilter and replaced standard Yii2 AccessFilter (on ControllersCommonTrait for all controllers) for more comfortable using Scopes. About it read below.

Scopes

As said in official OAuth2 Library docs:

The use of Scope in an OAuth2 application is often key to proper permissioning. Scope is used to limit the authorization granted to the client by the resource owner. The most popular use of this is Facebook’s ability for users to authorize a variety of different functions to the client (“access basic information”, “post on wall”, etc).

So, each token could have specific permissions to run corresponding API point. In out case, API point is actions.

In Yii2 we already have the tool to control access to particular action by user role, so I decided to expand it functionality to define scopes for action.

As result was created OAuth2AccessFilter which copy (not inherit, structure not allow to inherit) of standard AccessFilter logic to work with rules with additional logic to process scopes for private action by means of oauth2 module.

So, if user's role allows him to run action (API point), than will check if user's token has needed scopes to do it (if action has defined limited scopes). Example of using you can find in ProductController and it means that only authorized users with token which contain custom scope can have access to action custom.

yii2-oauth2-rest-template's People

Contributors

cyberinferno avatar ikaras avatar mtangoo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yii2-oauth2-rest-template's Issues

Extending Roles beyond ? & @ query on approach

About to try to add some additional roles (following the simpler approach to RB access mentioned in a few posts) So plan to override the already overridden AccessRules class to handle the additional roles and then update the controller rules for the specified roles. Have you any views on this approach?

Double code call

Hey, I've seen Your User::findIdentityByAccessToken(...) method and I don't think You should use the module like that, I mean, that code You call is already called before You enter findIdentityByAccessToken() method, so You are already authorized if You get there.
I've thought of using an activeRecord call to the model containing my token (I already have the token as a param) so my implementation of the method is like this:

use filsh\yii2\oauth2server\models\OauthAccessTokens;

public static function findIdentityByAccessToken($token, $type = null)
{
$oauthToken = OauthAccessTokens::findOne(['access_token' => $token]);
if ($oauthToken !== null) {
return static::findIdentity($oauthToken->user_id);
}
return null;
}

That gives me the userId I want and I don't have to repeat the whole authentication. I'm not really sure if that is the proper approach. For one I wouldn't want to access the model declared in a module explicitly. Sorry for the long rant ;)

Error - after updating Yii2

I updated my yii2 version and I get the following error

Setting unknown property: filsh\yii2\oauth2server\Module::options

Update Readme

Readme seem outdated and missing some thing.
If you can get time to update it it will be great

Set multiple scopes to an action

Hi, i was trying to set something like this

public function accessRules() { return [ [ 'allow' => true, 'actions' => [ 'index', 'view' ], 'roles' => ['@'], 'scopes' => ['default', 'admin'] ], ]; }

But, when i try to access with the eihter scope (default, admin) gime this error

{ "error": { "name": "Forbidden", "message": "You are not allowed to perform this action.", "code": 0, "status": 403, "type": "yii\\web\\ForbiddenHttpException" } }

When is only one scope all works fine.

Yii::$app->user->identity returns null

Hi @ikaras,
I am using yii2-advanced-template but implemented your logic for oauth2 scopes and permissions. I have created all the files like you and everything is working fine but i want to show "You need to login first" instead of "You are not authorized to perform this action" for requests without access token. As per Yii documentation after rest authentication you can grab the user identity from Yii::$app->user->identity but I am getting null even if the correct access token is passed.

Data returned irrespective of Rules/Scope set to restrict access

Hi there
Could you please shed any light on where I may be going wrong? Problem is that access restrictions are not functioning for the protected and custom scopes, so any call made returns data.

User table & oauth tables set up, populated and all working. Access Token requests for all scopes are working fine & the tables are updated too. My Controller has the same access rules as you have in ProductController.php e.g.

public $modelClass = '\api\common\models\Video';
    public function accessRules()
    {
        return [
            [
                'allow' => true,
                'roles' => ['?'],
            ],
            [
                'allow' => true,
                'actions' => [
                    'view',
                    'create',
                    'update',
                    'delete'
                ],
                'roles' => ['@'],
            ],
            [
                'allow' => true,
                'actions' => ['custom'],
                'roles' => ['@'],
                'scopes' => ['custom'],
            ],
            [
                'allow' => true,
                'actions' => ['protected'],
                'roles' => ['@'],
                'scopes' => ['protected'],
            ]
        ];
    }
    public function actionCustom()
    {
        return ['status' => 'ok', 'underScope' => 'custom'];
    }
    public function actionProtected()
    {
        return ['status' => 'ok', 'underScope' => 'protected'];
    }
}

example of a call with protected Access Token:
http://localhost:8085/v2/videos/8?access_token=e78354aae150c3a75507c86de11610badd4ab74d
image

If I call http://localhost:8085/v2/videos/8/protected I get a Not Found (#404)
image

sample of the main.php

'controllerNamespace' => 'api\controllers',
        'defaultRoute' => 'video',
    'modules' => [
        'v1' => [
            'basePath' => '@app/modules/v1',
            'class' => 'api\modules\v1\Module'
        ],
        'v2' => [
            'basePath' => '@app/modules/v2',
            'class' => 'api\modules\v1\Module'
        ],
        'oauth2' => [
            'class' => 'filsh\yii2\oauth2server\Module',
            'tokenParamName' => 'accessToken',
            'tokenAccessLifetime' => 3600 * 24,
            'storageMap' => [
                'user_credentials' => 'api\models\User',
            ],
            'grantTypes' => [
                'user_credentials' => [
                    'class' => 'OAuth2\GrantType\UserCredentials',
                ],
                'refresh_token' => [
                    'class' => 'OAuth2\GrantType\RefreshToken',
                    'always_issue_new_refresh_token' => true
                ]
            ]
        ],
    ],
    'components' => [        
        'user' => [
            'identityClass' => 'api\models\User',
            'enableAutoLogin' => false,
            'enableSession' => false
        ],
        'authManager' => [
                    'class' => 'yii\rbac\DbManager',
        ],          
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'urlManager' => [
            'enablePrettyUrl' => true,
            'enableStrictParsing' => true,
            'showScriptName' => false,
            'rules' => [
                'POST /oauth2/<action:\w+>' => 'oauth2/default/<action>',
                [
                    'class' => 'yii\rest\UrlRule', 
                    'controller' => ['v1/video', 'v2/video'],
                    'extraPatterns' => [
                            'GET custom' => 'custom',
                            'GET protected' => 'protected',
                            ],
                    'tokens' => [
                        '{id}' => '<id:\\w+>'
                    ]

                ]
            ],        
        ],
        'request' => [
                'parsers' => [
                        'application/json' => 'yii\web\JsonParser'
                ],
                'enableCookieValidation' => true,
                'cookieValidationKey' => 'nnnnnnnnnn'
        ],          
        'response' => [
            'class' => 'yii\web\Response',
            'formatters' => [
                yii\web\Response::FORMAT_HTML => '\api\components\HtmlResponseFormatter',
            ],
            'on beforeSend' => function (\yii\base\Event $event) {
                /** @var \yii\web\Response $response */
                $response = $event->sender;
                // catch situation, when a controller hasn't been loaded
                // so no filter was loaded too. Need to understand in which format return result
                if(empty(Yii::$app->controller)) {
                    $content_neg = new \yii\filters\ContentNegotiator();
                    $content_neg->response = $response;
                    $content_neg->formats = Yii::$app->params['formats'];
                    $content_neg->negotiate();
                }
                if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {
                    $response->data = [
                        'success' => $response->isSuccessful,
                        'data' => $response->data,
                    ];
                    $response->statusCode = 200;
                }
            },
        ],
    ],
    'params' => $params,
];

hi, how to use OAuth2\ClientAssertionType\httpbasic

i got error

PHP Fatal Error – yii\base\ErrorException

Call to undefined method OAuth2\ClientAssertionType\HttpBasic::className()

use OAuth2\ClientAssertionType\HttpBasic;

74    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return ArrayHelper::merge(parent::behaviors(), [
            'authenticator' => [
                'class' => CompositeAuth::className(),
                'authMethods' => [
                    ['class' => HttpBasic::className()],
//                    ['class' => HttpBearerAuth::className()],
                    ['class' => QueryParamAuth::className(), 'tokenParam' => 'accessToken'],
                ]
            ],
            'exceptionFilter' => [
                'class' => ErrorToExceptionFilter::className()
            ],
        ]);
    }
2. yii\base\ErrorHandler::handleFatalError()

alternative login/register from another OAuth i.e.

Actually it is quite cool to use this as an auth service that handles different apps or resource servers. I want to use it as an central auth instance. So the only point that is missing actually for it is that I can login/register alternatively with Facebook or Google or something like this at the Auth Service.

I did this quite easy with my own rest app. But here it is more difficult because the login does not have an email or password. So providing an alternative route for the login with another Auth service seems logical and I could generate the key and death counter by my own.

But this seems messy.
Do you have any better thought how to integrate it in your existing flow, so that this ecosystem is able to be connected by other ecosystems like FB or Twitter or what else?

Used in accordance with the tutorial configuration, there are 400 errors

I execute the following commands:
curl -i -H "Accept:application/json" -H "Content-Type:application/json" -XPOST "http://127.0.0.1:9093/oauth2/token" -d {"grant_type":"password","username":"tgy3300","password":"123456","client_id":"testclient","client_secret":"testpass"}

Prompt:
<b>Deprecated</b>: Automatically populating $HTTP_RAW_POST_DATA is deprecated a nd will be removed in a future version. To avoid this warning set 'always_popula te_raw_post_data' to '-1' in php.ini and use the php://input stream instead. in <b>Unknown</b> on line <b>0</b><br /> <b>Warning</b>: Cannot modify header information - headers already sent in <b>U nknown</b> on line <b>0</b><br /> {"name":"Bad Request","message":"The grant type was not specified in the request ","code":0,"status":400,"type":"filsh\\yii2\\oauth2server\\exceptions\\HttpExcep tion"}

@ikaras

Which is the yii2-oauth2-server version used?

Hi, I've just installed the template, but seems the yii2-oauth2-server was updated to latest (composer is marked as *) and broke compatibility with this application. What is the original version number? Thanks!

Column name is BigInt?

File: /application/api/migrations/m150504_104847_create_products_table.php
Column name is type bigint or type text?

few doubts

Hi,

Thanks for your great effort. i am using your template for my api and have couple of doubts. Hope you help me clear those :

  1. How to allow some methods to be public (for guest users) in same controller without oauth2 token.
  2. How to handle api request which has expired token?
  3. The two productController is really confusing me, would be great if you can write article on template flow.
  4. How to validate token manually in some cases?
  5. How to expire token forcefully?

Thanks

configuration issues!

hi,Ihor.Glad to meet you, I'm a PHP rookie from China, see your ikaras/yii2-oauth2-rest-template project on GitHub, but I have installed the configuration, but still running is not correct. I would like to ask you a good configuration of the entire project demo can send me, thank you. My email [email protected]. thank you.

How Scopes work as Access Control

Since this acts as template, its good to explain how Access Control, specifically Scopes works and what to do If someone wants to use RBAC with your template

Customize errors

Hi,
How to customize error messages/return?
I need to remove
"type":"yii\web\ForbiddenHttpException"
from status 403...
any idea?

thanks all.

Authorization client by client id and secret

Hi, is it possible to authorize client app passing client id and client secret in request header? I do not want to retrieve access token by client credentials, but only protect my Rest API with client id and secret.

Connection refused on migrate

Hi, I hope you can help me.
When I run the migration, the first one, I get an error 'Exception 'yii\db\Exception' with message 'SQLSTATE[HY000] [2002] Connection refused'
This is the command:
/Applications/MAMP/bin/php/php5.6.10/bin/php ./yii migrate --interactive=0

and this is the db config:

    'db' => [
        'class' => 'yii\db\Connection',
        'dsn' => 'mysql:host=127.0.0.1;port:8889;dbname=asc_auth;unix_socket=/Applications/MAMP/tmp/mysql/mysql.sock',
        'username' => 'myuser',
        'password' => 'mypassword',
        'charset' => 'utf8',
    ],

Same error without the unix_socket part.

I checked with another Yii2 template that the db connection is working with that string.

Thank you

I get 404 on each request

I followed the instructions and everything is going fine / installation/ db connection / migration than I tried this in terminal:
curl -i -H "Accept:application/json" -H "Content-Type:application/json" "http://api.loc/v1/products"

Maybe I am just foolish but I do not get to the point why it cannot find something.

The only thing that is different that I do not use api.loc instead I use the real path after localhost to the directory.

get 400 Bad request when request to get token

After config the project. I test request public api point. It works. But when I test request to get token, it got

HTTP/1.1 400 Bad Request
Host: localhost:6666
Connection: close
X-Powered-By: PHP/5.6.8
Content-Type: application/json; charset=UTF-8

{"name":"Bad Request","message":"The grant type was not specified in the request","code":0,"status":400,"type":"yii\\web\\HttpException"}~/Code/PHP/yii2-oauth2-rest
[15:21:57]--->

User registration

Hey, another thing I'm tackling right now is user registration, not sure how to add data OAuth2 module while registering a user, could You write something about it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.