I created a way to authenticate a user with API keys, thanks to a class A implementing the SimplePreAuthenticatorInterface interface. Everything works well (the user is successfully authenticated).
I'm trying to store the API keys, for a later use during the user's journey. To do so, inside the authenticate method of my class A, I return a PreAuthenticatedToken in which the credentials are my API keys.
The problem is : Inside a custom service, when I try to get the token credentials, I get null... I successfully get the API keys when I comment the line 76 of the PreAuthenticatedToken Symfony core class :
public function eraseCredentials()
{
parent::eraseCredentials();
//$this->credentials = null;
}
My questions are:
1) Why is the method eraseCredentials called whereas the user is authenticated? I thought this method was called during user's logging out...
2) What am I doing wrong? Is the PreAuthenticatedToken token the right place to store my API keys? How can I get them back from a custom service ?
Thanks for helping me. :)
PS : I'm a newbee on posting in Stackoverflow (and in English ^^). Sorry in advance for any mistakes.
I found another similar question but it has no helping response and I added some more precisions.
EDIT: The code of my custom service where I try to get the credentials is:
$token = $this->container->get("security.token_storage")->getToken();
if ($token !== null) {
$credentials = $token->getCredentials();
// $credentials is null here
}
EDIT 2: The return part in my code of my SimplePreAuthenticatorInterface::authenticateToken method :
return new PreAuthenticatedToken(
$user,
$apiKeys,
$providerKey,
$user->getRoles()
);
Ad 1. It depends on your AuthenticationProviderManager: this class accepts $eraseCredentials as second parameter - by default set to true (here).
That's why eraseCredentials method is being called on PreAuthenticatedToken $token during authenication (here).
Ad 2. Please check How to Authenticate Users with API Keys tutorial. You should create your custom ApiKeyAuthenticator class and add logic there.
According to your comment:
Note that authenticateMethod from tutorial is being called inside authenticate method (here). At that time token credentials are not erased yet and you can access them. For security reason they are erased after authentication (but this can also be changed / configured via security.yml file). If you need them later you can introduce custom token class and store API key there.
Related
I'm writing a Feature test for an API and I want to test custom auth logic.
I know that when I call the login API endpoint, Laravel caches the fact that the user is logged in so the next API call in the test would consider the user already authenticated...
So for one test, how do I disable this Laravel magic of auth caching so I can manually provide the Bearer auth token to check if my custom authentication logic works?
I'm thinking something along the lines of clearing the guards in AuthManager, or clearing AuthManager entirely so Laravel would be force to reinitialize it. But I'm having no luck in figuring out how to do that in a way that works.
Here's some pseudo-example code:
public function testLogin()
{
$responseData = $this->post('/login', ['email' => $this->user->email, 'password' => 'password'])->json();
$this->get('/endpoint-requiring-auth')->assertOk();
//
// $this->logicToResetAuth() ?
//
$this->get('/endpoint-requiring-auth')->assertUnauthorized();
// I want to manually have to pass the token to get it to work now.
$this->get('/endpoint-requiring-auth', ['Authorization' => "Bearer $responseData[access_token]"])
->assertOk();
}
Also the unknown reset logic should affect multiple guards.
Oh yeah, I'm writing custom logic around the JWT-Auth library.
For Laravel 8.x (and maybe newer)
auth()->forgetGuards();
But for JWT you may need to additionally do:
app('tymon.jwt')->unsetToken();
Or
app()->instance('tymon.jwt', null);
For Laravel 7.x (and maybe older)
As ->forgetGuards() is not invented yet in this version, and guards-array is protected, do something like:
/** #var \Illuminate\Auth\AuthManager $auth */
$auth = app('auth');
$mirror = new \ReflectionObject( $auth );
$prop = $mirror->getProperty( 'guards' );
$prop->setAccessible(true);
$prop->setValue($auth, []);
$prop->setAccessible(false); // Back to protected.
But for JWT, do same as above Laravel 8 section.
For jwt-auth, you can do this to clear the auth user:
auth()->forgetGuards();
app()->instance('tymon.jwt', null);
The first line, discards the existing cached guards. And the second, ensures when the guard is re-created, it wont reuse the same underlying jwt instance.
Good question. I was also struggling with this too. Yesterday I performed a deep dive on this subject. Turned out that TokenGuard has en implementation problem as the authenticated user is not refreshed after a new request is loaded. In order to fix this the following is needed:
Extends TokenGuard class into your own class and overload method setRequest. Add '$this->user = null;' before calling the parent implementation by 'return parent::setRequest($request);'
Use Auth::extend to be able to use your custom TokenGuard
After that, every new request resets the authenticated user automatically
See also https://code.tutsplus.com/tutorials/how-to-create-a-custom-authentication-guard-in-laravel--cms-29667
The user can log in, but i need his id to show his profile. Since i'm working in symfony 4, the many possible answers i've found were obsolete.
https://symfony.com/doc/current/security.html#retrieving-the-user-object
After authentication, the User object of the current user can be accessed via the getUser() shortcut (which uses the security.token_storage service). From inside a controller, this will look like:
public function index()
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$user = $this->getUser();
}
The user will be an object and the class of that object will depend on your user provider. But, you didn't tell us what user provider you are using, so I have no way of telling you, beyond this, how to get to the id itself.
One other way to get the user:
An alternative way to get the current user in a controller is to type-hint the controller argument with UserInterface (and default it to null if being logged-in is optional):
use Symfony\Component\Security\Core\User\UserInterface\UserInterface;
public function indexAction(UserInterface $user = null)
{
// $user is null when not logged-in or anon.
}
This is only recommended for experienced developers who don't extend from the Symfony base controller and don't use the ControllerTrait either. Otherwise, it's recommended to keep using the getUser() shortcut.
I trying to use lumen for the first time to create a restful backend service.
I'm used to working with laravel but with lumen I'm already stuck at the autentication. I can't find any tutorials on this.
I'm not even sure if my logic is secure for this. Bassically I receive a post request which contains an email and a password, then I want to check if the details are correct etc and authenticate the user.
I feel like I'm missing something, is this something that lumes comes with standard or will I need to rewrite the Auth service
It seems to be in the documentation you linked.
$this->app['auth']->viaRequest('api', function ($request) {
// Return User or null...
});
The Request class is passed in to this function. You would need to grab the email and password out of it $request->get('email') and request->get('password'), check to make sure they are valid.
I'm not sure of the best way to do this with Lumen or how much is available so to make it easy, you could just do something like the following...
$this->app['auth']->viaRequest('api', function ($request) {
$email = $request->get('email');
$password = $request->get('password');
$user = \DB::table('users')->where('email', $email)->first();
// Invalid Email
if ($user === null) {
return null;
}
// Check if password matches
if ( \Hash::check($user->password, $password) ) {
return $user;
}
// Invalid password
return null;
});
Keep in mind Lumen does not support session state you would need to pass in the email and password for every request. However, once it's setup, all you need to do in Lumen is use Auth::user() to grab the user.
You could also use jwt-auth which uses JSON Web Tokens which also makes it fairly easy and allows you to not pass emails and password around.
https://github.com/tymondesigns/jwt-auth
For anyone who encounters this problem. This is how i solved it:
In the auth serviceProvider (boot method) you check if there is a authorization header present. If there is one, it should include a apiToken, witch you can validate and continue with the normal flow.
If there is no Authorization header present, you can check the request variable for a email and password. Validate the login, and on success you save a new apiToken. I returned this token to the frontend, and made a feature that handles all ajax request, to include this token in the header. I also implemented a function that handles every response in my frontend application witch checks for a 401, when its there redirect to the login page.
With this aproach, you can use both auth methods, and Auth::user() is available through your application. Just make sure the login page is not handled with the Auth middleware!
My team has an API we wrote in PHP using the Slim Framework. It's being consumed by a web app and a third party mobile app.
We use the standard OAuth 2 workflow to provide access to our resources. We can see if someone is a sending a valid access token along with the API request and that part of the flow makes sense.
The stumbling block we're running into is how to most efficiently authorize access to a resource depending on the permissions of the user associated with the access token.
For the sake of simplicity let's say that we just want to ensure that the owner of the resource in question matches the owner of the access token. To me that seems like something that a route middleware would handle, for every request, before processing the request make sure that the resource owner ID matches that of the access token.
The problem in my mind is that resource permissions aren't necessarily consistent across all routes, a route isn't necessarily going to have an ID in the same section of the URI, it might not have an ID in the URI at all, etc. etc.
My current solution has been to have an authorization utility class that takes in an email and checks it against the user that's currently "logged in" (token owner).
class Authorization() {
checkResourcePermissions($email) {
if (loggedInUser->email == $email) {
return true;
}
}
}
Obviously this is a simplification, but what this means is that since a route middleware won't have the context of a request until that request goes through I will need to call this authorization method inside of every API route, responding with an error if necessary, etc. Our API is fairly large, essentially boiling this down to a large amount of copy and paste which always makes me very nervous.
What am I missing?
I'd suggest going the way using a 'normal' MiddleWare:
First, let's assume you have a class 'user', where you implement your logic to access various types of resources or has specific permissions, given only the $app. It can access the request and get any information from a the HTTP request necessary to identify a single user.
class User{
private $userid=false;
public function __construct($app){
$this->app=$app;
$users_mail=$this->app->request->post('email');
//SELECT user_id FROM USERSTABLE WHERE email = $users_mail
//$this->userid=user_id
}
public function ensureHasAccess($resourceID,$redirect='/noPermission'){
if(!/*some DB Query which returns true if this user may access the given $resourceID*/)
$this->app->redirect($redirect)
}
public function ensureHasRole($role) {
//some other permission checking, like ensureHasAccess();
}
public function ensureIsUser(){
//do Something
}
}
Next, you'll need a MiddleWare which is run before the route is dispatched, and creates an User-Object:
class UserPermissionMiddleware extends \Slim\Middleware{
public function call(){
$this->app->request->user = new User($app);
$this->next->call();
}
}
Then, simply add this Middleware:
$app->add(new UserPermissionMiddleware());
Now, you can modify every route to check the access to a resource, while the resource id (of course) still needs to be supplied by hand, according to the route-specific code:
$app->get('/resource/:id',function($resourceId) use ($app){
$app->request->user->ensureHasAccess($resourceId);
//deliver resource
});
If the user is not allowed to access this resource, the whole process is interrupted (Note that redirect() is implemented using Exceptions!) and another route is executed. You'll still need some code in every single Route, but if there's no consistent way to determine the resource to be acccessed, there'll be no way around this.
Hope this helps you so far. If you have any Questions, feel free to ask!
I implemented FOSOAuthServerBundle in my application to provide an API for mobile application. It seems to work, the user gets the Token and also the RefreshToken but if I make the controller require the user object that always returns null. Also fails the control of the permission, which is only IS_AUTHENTICATED_ANONIMOUSLY.
example:
public function showUserAction(){
$user = $this->getUser()
//$user = null
$auth = $this->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')
//$auth = false
}
How to get the user object in a controller with this type of authentication?
Based on your comment you have created client with different grant_types.
NOTE: If you use grant_type=client_credentials during authentication, there is no User available since you do not specify exact user/password for generating token. In case of grant_type=password you pass username & password and token is linked to user in this case. Hope this makes sense.
Try this:
$user = $this->get('security.token_storage')->getToken()->getUser();