Yii RESTful authentication - php

I am having trouble finding a method for authenticating API users in my application. I set out to initially build a system that users could access and authenticate through the web, but requirements have changed and I need to implement some additional actions that can be available in a RESTful manner using a POST API call.
I have created a class that extends CBehaviour and forced a redirect to the login page for all unauthenticated users (found on the yii framework forum here). Problem is that all API calls are forced through the same logic and any POST requests simply spit out the HTML to the login page.
class ApplicationBehavior extends CBehavior {
private $_owner;
public function events() {
return array(
'onBeginRequest' => 'forceGuestLogin',
);
}
public function forceGuestLogin() {
$owner = $this->getOwner();
if ($owner->user->getIsGuest())
$owner->catchAllRequest = array("site/login");
}
}
How would I go about separating the authentication of API users from the Web users?

I would follow this guide on creating a REST API in Yii. After modifying the config urlManager entries, all of your API requests will use the APIController. You can then place the following code in the beforeAction of your APIController to return nothing if the user is a guest (Or an error message)
protected function beforeAction($event) {
if (Yii::app()->user->isGuest) {
echo "Invalid credentials";
Yii::app()->end();
}
}
Note: The code above works for my purposes because all REST requests are sent via the same browser. (Which is already logged in and has a login cookie)
If you replace that behavior with a new base controller placed in protected/controllers to force login, it will only apply to your pages that require a login and not your APIController. Here is an example of mine:
//Make sure all Controllers which require a login inherit from this
class ControllerLoginRequired extends CController {
public function runAction($action) {
if (Yii::app()->user->isGuest && 'site' != $this->route) {
Yii::app()->user->returnUrl = $this->route;
parent::redirect(array('site/login'));
} else {
parent::runAction($action);
}
}
}
Everything explained will work for REST requests via the same browser in which the user has logged onto Yii. If you will have the need to expose your REST service to consumers that are not a browser logged into Yii, I believe you would have to come up with a custom authentication/token scheme.

Related

How to Restrict controllers or routes for different type of users in API

I'm working on a project with laravel. in my project there's two type of users one of them are admins and other one is normal users.
btw project is only provides API and there's no blade views.
I give a token to any user or admin logins with the api. and application will identify user or admin by sending that token with an authorization header and I check if token is validate and the user type is admin then give access to the admin features for that client.
here's my code for this part:
$admin = Auth::guard('admin-api')->user();
if ($admin) {
// allow to use admin features
}
else {
return response()->json(['error' => 'token is invalid'], 401);
}
I read something about applying Restrictions on a controller class in laravel and it was written there to add a constructor like this into controller class:
public function __construct() {
$this->middleware('admin-api');
}
and also there's something like that just for Restricting routes. like this
but I just want to know is it necessary to add same constructor to my controller class while the project just provides API? or the way that I'm doing is correct?
You are doing it right.
I would prefer restricting the request via routes, so there is no need to add constructor on each new Controllers.
Route::middleware(['admin-api'])
->group(function () {
Route::get('cart', 'Carts\CartController#retreive');
Route::post('cart/coupon', 'Carts\CartCouponController#addCoupon');
Route::delete('cart/coupon', 'Carts\CartCouponController#deleteCoupon');
Route::delete('cart/flyer', 'Carts\CartController#deleteFlyer');
});
This will apply the admin-api middleware on all the routes in the group and there is no need to add a constructor on Carts\CartController and Carts\CartCouponController, just to have middleware restriction.

Error with Laravel and phpCAS cannot redirect on failed authentication.

I implemented phpCAS as a Service Provider in my app that is running Laravel 5.1. Every single page in my app needs to be authorized so I created a Helper function that authenticates with phpCAS on my main app.blade.php file. The Helper function authenticates and then retrieves user information from my database so I can get user properties (id, fullName, etc.)
Here is the helper function:
public static function full_authenticate() {
Cas::authenticate();
$username = Cas::getCurrentUser();
$userinfo = Users::where('username', '=', $username)->first();
return $userinfo;
if (!is_null($userinfo)) {
return "ERROR." //THIS DOES NOT WORK
} else {
return View::share('userinfo', $userinfo);
}
}
This is how it's used in app.blade.php (as an example)
{{ Helpers::full_authenticate()->fullname }}
The authentication works perfectly and if you are in the Users model, it will load up your information no problem. The issue I am having is when you authenticate successfully in CAS, but are not in the Users table, I want to be able to throw a 403 error or redirect to another view or just say some sort of message, but no matter what I put in the "return" section, it doesn't work. I think it has something to do with my helper function running behind a controller, but I have no idea.
Also looked into integrating CAS into middleware, but not sure how to pursue that.
Any ideas would be awesome! Thanks in advance!

CakePHP Basic Auth redirect on browser

I am developing with CakePHP (2.5.6) and my goal is to use both form and basic auth.
I need this because I call some rest actions from a android app with basic auth. If the user visits the website with a browser I want to use form auth.
I set up my AppController to support both and this works fine. But if I access an action that requires authentication the basic auth alert pops up.
The best way would be to redirect to users -> login. But the basic auth alert pops up first.
How can I get around this? Is there a better solution?
I solved the problem by my own.
I have added a route prefix for api calls.
Configure::write('Routing.prefixes', array('api'));
in core.php file.
Now i can trigger api calls in the AppController's beforeFilter and add Basic authentication only for the api calls.
public function beforeFilter()
{
if(isset($this->request->params['api']))
{
// api call
// Pass settings in
$this->Auth->authenticate = array(
'Basic' => array('userModel' => 'User')
);
}
}
Example action.
PostsController -> index
public function api_index {
}
Url: domain.de/api/posts/index.json

Making user profiles with Kohana

I'm running Kohana 3, and having a hard time understanding the Auth module, or even if it's what I need. Basically I want to create a basic user profile site with basic username/password protection.
How do I take my existing controllers...
class Controller_Profile extends Controller
{
function action_index( $user_id )
{
// User should already be authenticated by here I think
}
}
...and use them with some sort of authentication system
For Kohana 3 you'll want to do your check in before and not __construct like JIStone suggests.
public function before()
{
parent::before();
// This automatically checks for an auto login cookie (thanks kemo).
if ( ! Auth::instance()->logged_in())
{
// Redirect to a login page (or somewhere else).
$this->request->redirect('');
}
}
Simple enough to understand. You can put this into a controller and have all the controllers that need authentication to extend that.
If you will be requiring a user to be registered for all pages on the controller you can put a check in your __construct() statement:
function __construct()
{
//Check roles for Access!!!!
parent::__construct();
$this->load_user();
if( ! $this->is_registered )
{
if(request::is_ajax())
die('This ajax call cannot be completed due to permission issues.');
// this will redirect from the login page back to this page
$this->session->set('requested_url', url::current());
url::redirect('user/login');
}
}
This is the code we use, but it is Kohana 2, not 3, so you will need to adjust a bit for your purposes.
I have provided a link to a short walkthrough for the installation and basic usage of the Auth Module in Kohana 3
Once you have your Auth process working, you can protect certain controllers by checking for a logged in user and proper authentication role in your before() method, or create a base controller for all your controllers that will need this check. If the user is not logged in, redirect them to the login page, if they do not have the proper access level (or role), then you can show them an "Access Denied" page.

Zend_Auth best practices

My goal is to require login for certain pages. I am using Zend Framework MVC, and I'm trying to find examples regarding best practices.
Some notes on what I'm looking for:
I want non-logged in users to get a login box, and then return to logged in version of the page, once authenticated
I want to use dependency injection, and avoid singletons
Small code footprint - tie into Zend mvc structure
Should login box be a separate controller and do header redirect? How to return to landing page after auth success? An idea to simply call the login controller action to display the login box in the landing page, or is this a disadvantage regarding search engine indexing?
Be able to use external library for handling cookies
Or something completely different. I'm fairly new to the Zend framework, and I want to do it 'the right way'.
I want non-logged in users to get a login box, and then return to logged
in version of the page, once
authenticated
Use a FrontController plugin and redirect or forward them to your loginAction.
I want to use dependency injection, and avoid singletons
Zend Framework, doesn't currently ship any DI system, however, the Zend_Application_Resource_* actually replace it. What kind of dependency would you need here?
Small code footprint - tie into Zend mvc structure
That's up to you.
Should login box be a separate controller and do header redirect? How
to return to landing page after auth
success? An idea to simply call the
login controller action to display the
login box in the landing page, or is
this a disadvantage regarding search
engine indexing?
I mostly use a special AuthController with LoginAction & LogoutAction. To redirect the user to the page is was trying to view, I always add a returnUrl element in my forms, and I inject the value of the requested URL to be able to redirect the user, and if none, I redirect him to the index/dashboard, depends.
Be able to use external library for handling cookies
Zend_Auth allows you to set your own storage mechanism, so just implement the interface.
$auth = Zend_Auth::getInstance();
$auth->setStorage(new My_Auth_Storage());
But never store authentication result in a cookie, it's so easy to modify it and access your website.
You may also take a look to one of my previous answer.
You could use the combination of Zend_Auth and Zend_Acl. To extend the other answers I give a short example of how you can manage authentication using zend framework:
First you need to setup a plugin to predispatch all requests and check if the client is allowed to access certain data. This plugin might look like this one:
class Plugin_AccessCheck extends Zend_Controller_Plugin_Abstract {
private $_acl = null;
public function __construct(Zend_Acl $acl) {
$this->_acl = $acl;
}
public function preDispatch(Zend_Controller_Request_Abstract $request) {
//get request information
$module = $request->getModuleName ();
$resource = $request->getControllerName ();
$action = $request->getActionName ();
try {
if(!$this->_acl->isAllowed(Zend_Registry::get('role'),
$module . ':' . $resource, $action)){
$request->setControllerName ('authentication')
->setActionName ('login');
}
}catch(Zend_Acl_Exception $e) {
$request->setControllerName('index')->setActionName ('uups');
}
}
}
So every user type has certain permissions that you define in your acl library. On every request you check if the user is allowed to access a resource. If not you redirect to login page, else the preDispatch passes the user to the resource.
In Zend_Acl you define roles, resources and permission, that allow or deny access, e.g.:
class Model_LibraryAcl extends Zend_Acl {
public function __construct() {
$this->addRole(new Zend_Acl_Role('guests'));
$this->addRole(new Zend_Acl_Role('users'), 'guests');
$this->addRole(new Zend_Acl_Role('admins'), 'users');
$this->add(new Zend_Acl_Resource('default'))
->add(new Zend_Acl_Resource('default:authentication'), 'default')
->add(new Zend_Acl_Resource('default:index'), 'default')
->add(new Zend_Acl_Resource('default:error'), 'default');
$this->allow('guests', 'default:authentication', array('login'));
$this->allow('guests', 'default:error', 'error');
$this->allow('users', 'default:authentication', 'logout');
}
}
Then you have to setup acl and auth in your bootstrap file:
private $_acl = null;
protected function _initAutoload() {
//...your code
if (Zend_Auth::getInstance()->hasIdentity()){
Zend_Registry::set ('role',
Zend_Auth::getInstance()->getStorage()
->read()
->role);
}else{
Zend_Registry::set('role', 'guests');
}
$this->_acl = new Model_LibraryAcl ();
$fc = Zend_Controller_Front::getInstance ();
$fc->registerPlugin ( new Plugin_AccessCheck ( $this->_acl ) );
return $modelLoader;
}
Finally in your authentication controller you have to use a custom auth adapter and setup actions for login and logout:
public function logoutAction() {
Zend_Auth::getInstance ()->clearIdentity ();
$this->_redirect ( 'index/index' );
}
private function getAuthAdapter() {
$authAdapter = new Zend_Auth_Adapter_DbTable (
Zend_Db_Table::getDefaultAdapter ());
$authAdapter->setTableName('users')
->setIdentityColumn('email')
->setCredentialColumn ('password')
->setCredentialTreatment ('SHA1(CONCAT(?,salt))');
return $authAdapter;
}
In your login action you need to pass login data to the auth adapter which performs the authentication.
$authAdapter = $this->getAuthAdapter ();
$authAdapter->setIdentity ( $username )->setCredential ( $password );
$auth = Zend_Auth::getInstance ();
$result = $auth->authenticate ( $authAdapter );
if ($result->isValid ()) {
$identity = $authAdapter->getResultRowObject ();
if ($identity->approved == 'true') {
$authStorage = $auth->getStorage ();
$authStorage->write ( $identity );
$this->_redirect ( 'index/index' );
} else {
$this->_redirect ( 'authentication/login' );
}
And that's all. I recommend you this HOW TO on youtube on zend auth and zend acl.
Not sure if this is best practice, but if I were to implement what you are after then I'ld do the following:
Create a LoginForm with a uniquely identifiable submit button (explained later)
Create an AuthService with methods getLoginForm, login, logout and one or all of: getIdentity, hasIdentity, and similar methods.
Create a viewhelper that has an instance of this service, and depending on what the outcome of hasIdentity is, either render the LoginForm, or render the identity of logged in user, in the views that need this functionality.
Create a front controller plugin that also has an instance of the service and implement the preDispatch plugin hook. Give it some config of which actions to probe. If AuthService->hasIdentity() returns true, do nothing. If request is a post request look for the uniquely identifiable submit button name, i.e.: $request->getPost( 'loginSubmitButton', null ); defaulting to null if not available. If not null do a login( $request->getPost() ) on the service. If successful, redirect to the same action, if unsuccessful fall through and render the form in the viewhelper again (automatically displaying the errors).
The service could be fetched by both the viewhelper and the front controller plugin with some service locator doing something like ServiceAbstract::getService( 'Auth' ) by registering the service to ServiceAbstract in the bootstrap.

Categories