I am using CakePHP as a REST API for a single-page app.
Every request gets authenticated and authorized before proceeding.
The problem is, on logging in, if the credentials are wrong, Cake returns 401 and the browser shows its own server log in a popup.
I believe there is a way to stop it by unsetting the WWW-authenticate header, but I need to know how. Can someone explain how to unset that header?
The headers are being set in the \Cake\Auth\BasicAuthenticate authentication adapter.
https://github.com/cakephp/cakephp/blob/3.0.11/src/Auth/BasicAuthenticate.php#L85-L110
It's hardcoded, so if you want to change this behavior, you'll have to create a custom/extended authentication adapter and override this behavior.
Here's a quick example:
src/Auth/MyCustomBasicAuthenticate.php
namespace App\Auth;
use Cake\Auth\BasicAuthenticate;
use Cake\Network\Exception\UnauthorizedException;
use Cake\Network\Request;
use Cake\Network\Response;
class MyCustomBasicAuthenticate extends BasicAuthenticate
{
public function unauthenticated(Request $request, Response $response)
{
throw new UnauthorizedException();
}
}
Controller
$this->loadComponent('Auth', [
'authenticate' => [
'MyCustomBasic'
]
]);
See also
Cookbook > Authentication > Creating Custom Authentication Objects
Cookbook > Authentication > Using Custom Authentication Objects
Related
I am building a REST user-microservice using Laravel 5.5 + Passport.
I am using the standard Passport::routes(), but I have had to modify the Auth::routes in order to make them return JSON responses, and to make them work with Passport.
I have added the following lines to my routes/web.php file:
Route::group(['middleware' => 'auth:api'], function () {
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
});
This allows me to POST https://myapi/logout
If I make the call with the header "Authorization => Bearer TOKEN", I get a successful logout response.
If I provide no header at all, I get a "not authenticated" message (which is good)
However, if I provide the header with a revoked token, I get a recursive deadloop of the function: Illuminate\Auth\RequestGuard->user() (it keeps calling itself recursively until stack-overflow)
This is all done in the auth:api middleware, my logout code is not reached, but my LoginController constructor is called. Constructor code:
public function __construct(Application $app)
{
$this->apiConsumer = $app->make('apiconsumer');
$this->middleware('guest')
->except('logout');
}
I'm struggling to understand if it's my code causing this issue, or some combination of Laravel + passport + auth.
My first thought was that the auth:api middleware fails to authenticate the user, and as a result redirects the user to /home, where for some reason it's triggered again, recursively. But if that was the case, why would it work correctly with no header?
My current thinking is that the token in question does exist in the database, but Laravel is failing to figure out that it's revoked.
Any suggestions appreciated,
I found an answer (if not the answer) after a lot of research. It appears this is a Laravel bug (https://github.com/laravel/passport/issues/440). The solution is to add OAuthServerException to the $dontReport array in app/Exceptions/Handler.php:
class Handler extends ExceptionHandler
{
protected $dontReport = [
...
\League\OAuth2\Server\Exception\OAuthServerException::class,
];
}
This will avoid trying to log user information, thereby avoid the deadloop.
I have faced this in localhost. in my case, I have used xampp server and facing this issue
after creating a virtual host like "testlarave.test" then solve the error
I'm writing an API in Slim 3 that integrates with a legacy system. The client sends a token to my API in the querystring. I want to write a middleware that will authenticate the token and return an object that contains the necessary internal login credentials (which are different from the token) that are used by the legacy system.
I can authenticate the token now, but my problem is that Slim 3 requires that the middleware return a \Psr\Http\Message\ResponseInterface instance. I also want it to return a custom object back to the application.
I think I can achieve this by re-verifying the token outside of the middleware, and only use the middleware as a way to authenticate the token and return an error if it fails. I tend to think this kludgy way could be avoided so I just have to use the token once in the middleware and return the custom object at the same time so I don't have to use the token twice.
I have searched around for solutions, but all of the example middlewares I can find are similar to https://github.com/julionc/slim-basic-auth-middleware, where they are simply authenticating in the middleware but do not have the requirement to return a custom object. The documentation at http://www.slimframework.com/docs/concepts/middleware.html doesn't seem to help much either with this custom requirement.
Any ideas?
You could include a callback in your middleware which you can use to store the custom object somewhere. For example with slim-jwt-auth you can use callback to store the decoded contents of JWT using a callback.
$app->add(new \Slim\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub",
"callback" => function ($request, $response, $arguments) use ($app) {
$app->jwt = $arguments["decoded"];
}
]));
Note that this kind of callback is not a Slim 3 feature. Just something this middleware happens to use.
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
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
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.