How to pass loggon user data object with laravel passport token? - php

I am new to laravel and angular. I m using Laravel Framework 7.10.2 and Angular CLI: 8.3.26
I configured laravel passport according to documentation. Now when user logged in my api sends access token to my angular app.like this(copied from developer console)
token:"eyJ0eXAiOixxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxx"
this is my laravel login method.
class UserController extends Controller
{
public function login(Request $request){
$login=$request->validate([
'email'=>'required',
'password'=>'required'
]);
if (!Auth::attempt($login)){
return response(['message'=>'Invalid login details.']);
}
$user=Auth::user();
$token=$user->createToken('authToken')->accessToken;
return response(['user'=>Auth::user(),'token'=>$token]);
}
Now I want to save this user id in local storage. But I know it is not secure. So Is it possible to send this user object from this token?
1)If is it possible how can I do this?
2)How can I read this(means user object) in the angular application?
This is my angular function to read the token.
login() {
this.authService.login(this.email, this.password).subscribe(res => {
//reading token comes from api
console.log(res);
if (res.token) {
this.token = res.token;
localStorage.setItem('auth', this.token);
this.router.navigate(['tasks'],);
} else {
this.errormessage = res.message;
}
this.authService.changeProgress(true);
}, err => {
this.errormessage='Connection failed! Try again later!';
this.authService.changeProgress(true);
});
I found some similar questions related to this. But they didn't work for me.
Thanks in advance.

I don't think you need to store all of this, the passport it self identify whit a user.
In my case I need the username to display in a navbar for that I use a service to get the data, maybe I could store it in the browser but that can be done if that info in the local storage is only for display, not if is an ID or something you need to make a request, that is highly insecure.
So, my advice, is to make a request to get the user info that you need.
I am starting on jwt passport but for what I have read you need the secret key to decode the token, that said I believe you can't decode it on the front-end.
Note: if someone has more info comment please

Related

Symfony token getCredentials returns null

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.

Proper way to check if user is authorized on the backend (laravel 5.4 & tymon-jwt)

I'm using Angular 4 and Laravel 5.4 & Tymon JWT. Currently I just pass the token from the frontend which is stored in local storage to the backend and Tymons default auth does its work. However I have some columns in my database like canView, canUpdate that I check before allowing a user to do something. I just parse the token, get the user from the token, and check the column. What is a better/safer way to do this?
doSomething(Request $request) {
$thisUser = JWTAuth::parseToken()->authenticate();
$user = User::find($thisUser->id);
if ($user->canUpdate) {
//allow them to update
}

Lumen - Non token-based authenthication?

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!

Yii2 Login Authenticate without password

I am working on project that will use API of another application. So I won't validate password on my app, will do this via API request. I don't want to store users` passwords in db. Just check password by API.
How can I authenticate a user without login credentials?
Login & Session in Yii 2 is managed by \yii\web\User class
This class is accessible in the application life cycle via the \Yii::$app->user property.
On successful authentication using your third-party provider you use the login() function using Yii::$app->user->login($identity)
$identity should be an object of [IdentityInterface][4] usually the User ActiveRecord model implementing this interface
Refer to the guide documentation on Authentication for full understanding on how authentication works in Yii2
In our app we are using a field auth_key related to a User Model and then we go to this link, find a user in database by this auth_key and make a login (or not if it is not found in database )
public actionAuthLogin($auth_key){
if(($user = User::findOne(['auth_key' => $auth_key]))
&& \Yii:$app->user->login($user)){
return $this->goHome();
} else {
throw new NotFoundHttpException()
}
}
then
/controller/auth-login?auth_key=should_be_in_database_ahsgdashdajsdg123mkasd

Restrict operations in restful PHP API

I am creating an AngularJS application with a restful API written in PHP as backend. This is the first time I'm using AngularJS and PHP "together".
Angular is keeping track of the authentication of users using the ngCookies module. Some operations, like deleting stuff, should only be available for users with specific privileges. How can I make sure that "normal" users or users that have not logged in cannot access the deletion operations of the API?
Any ideas are appreciated.
Here is how I do it.
In users DB table I add column named token VARCHAR(36)
Whenever user logs in:
I update lastlogin column
I update that token with MD5($ip.$email.$logindate)
Now I return user object to Angular and angular knows token.
In Angular $http service I add interceptors and before any request is made Authentication header is set. I use basic authentication. I create string $user_id.'::'.$token.
app.factory('authInterceptor', function($rootScope, $q, appConfig, $injector, $cacheFactory) {
function request(config) {
if(angular.isDefined($rootScope.currentUser.id)) {
config.headers.Authorization = 'Basic ' +
window.btoa($rootScope.currentUser.id + ':' +
$rootScope.currentUser.token);
}
return config;
}
function response(response) {
if(angular.isDefined(response.data.code) && parseInt(response.data.code) == 401) {
var UserApi = $injector.get('UserApi');
UserApi.logout();
$cacheFactory.get('$http').removeAll();
UserApi.login(response.data.message)
.catch(function() {
var $state = $injector.get('$state');
$state.go('app.home');
});
}
return response || $q.when(response);
}
return {
request: request,
response: response
};
})
This is my authInterceptor factory that I insert into app
app.config(function($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
})
What is happening there I set standard Authentication header for every request if user is authorised.
Then in PHP I get this header. I get user ID and Token separately. Then I use user ID to get user data from DB where I have token and last login date.
Now I can compare token and see if this user is the one who logged in.
But this is not absolutely secure. If anyone get this token, then he can login. That is why IP is used. not only I check the token against one in DB I also check it against IP. I create MD5($ip.$email.$logindate) because I know all that data and check against token that I get from angular. If it was sent from different IP it will not pass through.
You can also see function response in authInterceptor. Whenever I have authentication problem I send back HTTP code 401. Now in response I know that Authentication failed. I logout user and redirect him to homepage of the site.
Now it is very simple to code. You just return what have to be returned, and do not care about none authenticated user.
But there is more. If you need some kind of ACL, then you can design this as you wish. In your class that return particular RESTFull API method you can define $acl property and set name of the group. In the same place where you check for authentication, you can check for ACL too.
Please see my code here it is PHP backend and Angular frontend
https://github.com/Coach-Hub
This is the basic Idea, you can of course build around that.
I am not a php developer. You can not ensure this thing in front-end-side. Below is the sample code we use in NodeJs app, to validate user has valid permission of deletion or not.
router.patch('/:id', auth.hasRole(userEnums.roles.admin), controller.update);
router.post('/create', auth.hasRole(userEnums.roles.admin), controller.create);
//Checks if the user role meets the minimum requirements of the route
function hasRole(roleRequired) {
if (!roleRequired) throw new Error('Required role needs to be set');
return compose()
.use(isAuthenticated())
.use(function meetsRequirements(req, res, next) {
if (req.user.role.indexOf(roleRequired) !== -1) {
next();
}
else {
res.send(403);
}
});
}
Here auth.hasRole is simple method/middleware, which is a curry design pattern. It checks use req and checks that user has valid permission to delete. In case of user don't have admin permission it return back with unautherize error message.
This is for admin and user relation. We also use another strategy at the end to validate user. Suppose we have expose our delete API and anyone can delete it. In that case we have to ensure that active user has to be the owner of the document. In that case first we get the owner id of the document and match it with requested user id. If matches than we delete the document.
BlogPostApiService.destroy(id, _.curry(hasPermission)(req.user))
//hasPermission implementation
function hasPermission(user, blogPost) {
return user && (user.hasRole(UserEnums.roles.admin) || (user._id.toString() == blogPost.author.id.toString()));
}

Categories