Authenticate a user with my own function/api? - php

When user submits login form, for authenticating that user instead of using cakephp default authentication $this->Auth->login() I want to call some java based api where I will send user id and password entered by user and that api will return user details if user is authentic and false if not. I want to use cakephp AuthComponent for rest of thing (for example: sessions etc) but just for authenticating a user I want to call my api. Is this possible ? can I override some core method ?
Thanks.

I think this covers it, under "Creating Custom Authentication Objects"
http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html

You can create a class extending BaseAuthenticate in Controllers/Component/Auth folder path. There you have to implement an authenticate() method that, if logged successfully, have to return an array with user data, and if not, return false.
Then you tell Auth component to use your custom authenticate when you call it in AppController by:
'Auth' => array(
'authenticate' => array(
'yourNewAuth' => array(
//other stuff
)
)
)

you can set AuthComponent::$sessionKey to false to ensure AuthComponent doesn’t try to read user info from session.
http://api.cakephp.org/2.4/class-AuthComponent.html

Related

SimpleSAMLphp custom authsource without password

For one of our customers I need to write a custom authsource module. I allready set up SimpleSAMLphp as an Idp using various authsources like LDAP, SQLauth, etc. All of those authsources have in common that they use a login form and authenticate against the sspmod_core_Auth_UserPassBase class. This means that there will be a login form for username and password etc.
The special case right here is the following:
The SAML installation (IdP) is inside the companies network. If a user (inside of that network/authenticated via AD) visits a host that is inside that network, the username will be automatically injected in its browser and is available via $_SERVER['PHP_AUTH_USER'] and we have the guarantee, that the user is allready "validated".
This means we do not have to "authenticate" the user any more. We do not have to show a login form and just need to mark that user as authenticated.
Now I am a little stuck, because for my understanding it would be a little bit "overdosed" to write an own authsource that extends the default UserPassBase class. I would have to handle empty passwords and automatically forward/post from login form etc.
I guess there is a better way to handle that. The flow would be pretty simple:
SP redirects to IdP. The IdP "login page" reads PHP_AUTH_USER (no output like a login form), authenticates the user (without any further check) and redirects as expected if user was detected correctly. If the PHP_AUTH_USER could not be found in any way, the user will be redirected to some kind of error page.
Any ideas how to solve that the correct way? I guess I will have to write my own completely new authsource class that extends the base SimpleSAML_Auth_Source class? Maybe someone has an example for that situation before I am going to reinvent the wheel!?
Take a look at the exampleAuth:Static authsource. It auto-logs you in as a specific user
'example-static' => array(
'exampleauth:Static',
'uid' => array('testuser'),
'eduPersonAffiliation' => array('member', 'employee'),
'cn' => array('Test User'),
),
You could create your own module like it, but instead of loading attributes from the authsource config, load them based on the username. and do something like
public function authenticate(&$state) {
$user = $_SERVER['PHP_AUTH_USER'];
if ($user) {
$attributes = loadAttributesForUser($user);
$state['Attributes'] = $attributes;
} else {
throw new Exception('No user found');
}
}

Yii2 REST API Basic Auth (SESSION OVERRIDE)

I have implemented findIdentityByAccessToken in my Users model.
public static function findIdentityByAccessToken($token, $type = null)
{
$apiUser = ApiAccess::find()
->where(['access_token' => $token])
->one();
return self::findOne(['id' => $apiUser->idUser]);
}
In the browser, if i'm logged into the system, I can hit an api get endpoint, enter my auth token and be authenticated properly.
However, If i am not logged in, I get kicked back to my login screen. Using a rest client, I am returned the HTML of the login screen.
This indicates 1 of 2 things in my eyes. Either, in the current state, it is requiring a 'logged in session' in order to access that api module. Or #2, I'm not properly passing the auth token.
My Request header:
Accept: */*
Cache-Control: no-cache
Authentication: Basic base64('mytoken':)
How do I override my "default" login behavior? OR Properly send the authentication token?
You can override login method and loginByAccessToken from model User to change the login behavior. See: http://www.yiiframework.com/doc-2.0/yii-web-user.html
On the other hand, what you probably need (in case that you don't have it yet) is to write a controller and implement a login action. Then implement a class extending from AuthMethod and authenticate method (and maybe challenge method). After that you can add that class as a behavior to all your controllers (or even better make all your controller inherit from one controller with that behavior).
Plase take a look at this link: http://www.yiiframework.com/doc-2.0/guide-rest-authentication.html

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

CakePHP Stateless (Basic) Auth, where to call $this->Auth->login()?

My CakePHP v2.4.X app supports both Basic and Form authentication (Form is for web users, and Basic is for Stateless access from Android App).
AppController.php contains the following $components declaration:
public $components = array(
'Auth' => array(
'authenticate' => array(
'Basic',
'Form',
),
),
);
From the doc on performing stateless Basic Auth:
"In your login function just call $this->Auth->login() without any checks for POST data."
My issue is that if the user logs in using Basic Auth, they never trigger Users/login - so I am unsure where to place the $this->Auth->login() function.
Do I simply place this code in AppController/beforeFilter() and if the current user is not logged in I attempt login every time? ie:
if($this->Auth->loggedIn() == false)
{
$this->Auth->login();
}
This doesn't seem right to me because if the user is using Form login they'll end up calling $this->Auth->login(); twice [once from AppController/beforeFilter(), and again from UsersController/login()].
Also, when simply loading the login (via GET), the system will attempt to log them in and therefore return an error message.
I am also unsure how to determine if the user did login via Basic (as opposed to Form), and therefore set: "AuthComponent::$sessionKey" to false only when Basic was used.
Any help would be much appreciated.
The manual section related to basic auth doesn't correspond to what you are saying. Since 2.4 basic/digest auth doesn't need a login action at all. Just including Basic in the array for "authenticate" key for auth config is enough. It will automatically cause cake to check for required credentials when trying to access a protected action and if no credential or invalid credentials are provided appropriate headers are returned to the client.
Using both Basic and Form authenticators together can be problematic. I suggest modifying auth config in beforeFilter to use only either one of them conditionally by using appropriate conditions to check if request is from mobile app or not.

CakePhp Auth : Questions

I've to make a cakePhp authentification, and I wish to use the "Auth" component. I'm trying to see if it fill my requirement:
I need to authenticate users with their email OR their customerId, (with an addition password of course). I can't find if it is possible to have two fields(or more) on which the authentication can be done
I've several parts on which I need to be authenticated. But I need differents granularity:
For some things, it's the whole controller which should not be accessible(if possible with exception(e.g. all the "User" controller, except the login/register action) for other I really need that it's the whole controller(e.g. the cart controller)
Sometimes I need that only some actions are unavailable without being logged
Sometimes I need that only a part of the view isn't displayed(e.g. login element not displayed)
Does the component manage action like password change? Because if the user change its password I need that he did not get disconnected.
Thank you very much for your help
The short answer is that yes, you can do these things, but it seems to me that the ACL might be overkill for your needs (but I also tend to avoid the ACL if there's any opening at all to do so). To your points:
As Ivo suggests, you'll need a custom UsersController::login() method to authenticate by multiple fields (If your auth model isn't User, then use the appropriate controller). If the Auth component's login method fails, it passes control to your custom login() method. Here's a snippet from a project I've been working on:
function login() {
# Allow login by either username (legacy) or email.
# If no authenticated user exists at this point then the Auth
# component's login() method has failed and control has been passed
# here for any further handling. Since the expected credentials
# (email and password) have failed we're going to check for
# username and password.
$user = $this->Auth->user();
if( empty( $user ) && !empty( $this->Auth->data['User']['email'] ) && !empty( $this->Auth->data['User']['password'] ) ) {
$user = $this->User->find(
'first',
array(
'recursive' => -1,
'conditions' => array(
'User.username' => $this->Auth->data['User']['email'],
'User.password' => $this->Auth->data['User']['password'],
)
)
);
if( empty( $user ) || !$this->Auth->login( $user ) ) {
# Set the configured flash message b/c login failed after both checks.
$this->Session->setFlash( $this->Auth->loginError, null, array(), 'auth' );
}
}
$this->redirect( $this->Auth->redirect(), null, true );
}
For action access, just use the $this->Auth->allow() and $this->Auth->deny() methods in each relevant controller's beforeFilter() callback. For example, in the UsersController, you may want to do something like this:
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->deny('*');
$this->Auth->allow( 'login', 'logout' );
}
In views, just determine whether the user is authenticated by testing the Auth.User value to determine what to display/hide from anonymous/authenticated:
if( $this->Session->check( 'Auth.User' ) ) { ... }
If the password changes, you can re-authenticate the user transparently by calling $this->Auth->login( $user_data ). I do this, for example, when a user registers. I don't want him/her to have to then go login, so I just login automatically.
Auth doesn't do exactly what you want out of the box. It can only handle whether a user authentification is required to access an action or not. Once the user is logged in, Auth does not care about the user's level access anymore.
If you wish to use more than two fields, I suggest that you extend the AuthComponent and rewrite the login method to fit your needs. I never did it but I imagine that it is reasonably easy.
Regarding your access levels, I would use ACL that can manage access to actions of all the controllers. Once set up, you will have to manually set the permissions for each actions, either using one of the plugins written by the community or manually.
If you wish to disable part of your views you will need to read the permissions to test the user's access level from there. A good thing would be to save the permissions in a cache file or in the session as the user logs in to make it avaiable in the view. Then write your tests and echo what's needed.
(I am using CakePHP 2.0, I don't know how easily you can extend AuthComponent in 1.3 if you're using it)
With Auth, you need to have exactly 2 fields (that you can specify) to authenticate on. One field (password) will be hashed. Yes, all levels of access you want can be specified in Auth: http://book.cakephp.org/view/1251/Setting-Auth-Component-Variables
You'll have to manage the password change, but the users won't get logged out if they change password.
Sometimes I need that only a part of the view isn't displayed(e.g. login element not displayed)
wut?
Create a custom login() which tries to authenticate through either method.
You can also setup the Authenticate variable to do a custom login.
You can specify in different controllers what parts Auth should be allowing to authenticated users. See Auth methods.
You can also use ACL (see the full Cake ACL tutorial, etc) to control granular permissions.
Sometimes I need that only a part of the view isn't displayed
Create an element which checks for Auth->user() to select what content to display.
For the Allow and Deny part can be easily done with the Auth Component.
use something like
$this->allow('*'); // to allow every thing
$this->deny('*'); // to deny every thing
$this->allow('login', 'signup'); // allows login and sign up without being logged in
$this->deny('profile', 'password'); // need to be logged into to access profile and password and rest of the actions are allowed.
For the change of password you can save the changed password to the database and force logout the user and redirect him to login again
$this->Auth->logout(); this forces the user to logout of cakephp Auth
For the first question - loggin in using email or customer id is not directly possible using cakephp Auth component as you will have to define in particular which of them acts as a username.
Alternative solution is you can just copy the Auth Component into your app/controller/component and hack the code.
When you receive the user name you can test it against email and customer id and maintain the flow.
Hope this helps

Categories