Laravel passport create token and refresh token - laravel

I'm using Laravel and passport on my project.
In my project users can get token in two way.
First with username and password that its OK by passport.
Second with verification code that sent with SMS that its my problem.
I tried createToken() method but it will make a personal token without refresh token and I need to create token with refresh token and specify client id in a controller (without HTTP request).
$user = App\User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
How can I do this?

Create a new controller AccessTokenController that extends \Laravel\Passport\Http\Controllers\AccessTokenController
<?php
namespace App\Http\Controllers;
use App\User;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ServerRequestInterface;
use Response;
class AccessTokenController extends \Laravel\Passport\Http\Controllers\AccessTokenController
{
public function issueToken(ServerRequestInterface $request)
{
try {
//get username (default is :email)
$username = $request->getParsedBody()['username'];
//get user
$user = User::where('email', '=', $username)->firstOrFail();
//issuetoken
$tokenResponse = parent::issueToken($request);
//convert response to json string
$content = $tokenResponse->getBody()->__toString();
//convert json to array
$data = json_decode($content, true);
if(isset($data["error"]))
throw new OAuthServerException('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
//add access token to user
$user = collect($user);
$user->put('access_token', $data['access_token']);
$user->put('expires_in', $data['expires_in']);
$user->put('refresh_token', $data['refresh_token']);
return Response::json(array($user));
}
catch (ModelNotFoundException $e) { // email notfound
//return error message
}
catch (OAuthServerException $e) { //password not correct..token not granted
//return error message
}
catch (Exception $e) {
////return error message
}
}
}
You can generate client_id by using following command
php artisan passport:client

I found a package and I'm using that
https://github.com/qiutuleng/laravel-passport-phone-verification-code-grant
It's good.

Related

Add custom claim to Laravel Passport access token

I'm manually creating my Passport access tokens, see below.
How can I add custom claims to the created token?
In the past, where I didn't need a refresh token, I used this: https://github.com/corbosman/laravel-passport-claims
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ServerRequestInterface;
use Response;
use \Laravel\Passport\Http\Controllers\AccessTokenController as ATC;
class AccessTokenController extends ATC
{
public function issueToken(ServerRequestInterface $request)
{
try {
//get username (default is :email)
$username = $request->getParsedBody()['username'];
//generate token
$tokenResponse = parent::issueToken($request);
//convert response to json string
$content = $tokenResponse->getContent();
//convert json to array
$data = json_decode($content, true);
if(isset($data["error"]))
throw new OAuthServerException('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
//add access token to user
$token = array();
$token["access_token"] = $data['access_token'];
$token["expires_in"] = $data['expires_in'];
$token["refresh_token"] = $data['refresh_token'];
return Response::json($token);
}
catch (ModelNotFoundException $e) { // email notfound
//return error message
return response(["message" => "User not found"], 500);
}
catch (OAuthServerException $e) { //password not correct..token not granted
//return error message
return response(["message" => "The user credentials were incorrect.', 6, 'invalid_credentials"], 500);
}
catch (Exception $e) {
////return error message
return response(["message" => "Internal server error"], 500);
}
}
}

laravel 8 -> authorization with jwt and postman

I'm building an API with Laravel8 and use postman , Also I use JWT for authentication and want to authorization for creating a new post , I want to be able to create a new post only when the user is an admin or author , so I create a middleware AdminLevelAuthorization and in kernel I added it as 'auth.level'
So I put this middleware for my route in api.php : Route::apiResource('posts' , PostController::class)->middleware('auth.level:admin,author');
In postman I logged in and saved my token, I just don't know how use this token for authorization
When I go to this route http://localhost:8000/api/posts in postman and in authorization part , bearer token type I enter my token in token field , so it says :
"You are unauthorized to access this resource"
I don't know I'm wrong or not in entering token or my middleware is the problem
This is my middleware :
class AdminLevelAuthorization
{
public function handle($request, Closure $next, ...$levels)
{
try {
//Access token from the request
$token = JWTAuth::parseToken();//Try authenticating user
$user = $token->authenticate();
} catch (TokenExpiredException $e) {
//Thrown if token has expired
return $this->unauthorized('Your token has expired. Please, login again.');
} catch (TokenInvalidException $e) {
//Thrown if token invalid
return $this->unauthorized('Your token is invalid. Please, login again.');
} catch (JWTException $e) {
//Thrown if token was not found in the request.
return $this->unauthorized('Please, attach a Bearer Token to your request');
}
//If user was authenticated successfully and user is in one of the acceptable levels, send to next request.
if ($user && in_array($user->levels, $levels)) {
return $next($request);
}
return $this->unauthorized();
}
private function unauthorized($message = null){
return response()->json([
'message' => $message ? $message : 'You are unauthorized to access this resource',
'success' => false
], 401);
}
}
Thank you for your help :)
the problem was in s for user->levels it must be $user->level

Tymon\JWTAuth::toUser error: A token is required

I have a Larvel API that uses Tymon\JWTAuth to authenticate the user.
It is working fine.
For some reasons I have also a non-guarded route in web.php:
Route::get('myroute', 'MyController#mymethod');
The code of MyController#mymethod is the following:
$user = JWTAuth::toUser($request->input('token'));
// I tried also this:
// JWTAuth::setToken($request->input('token'));
// $user = JWTAuth::authenticate();
And I call the route using this url in the browser: /myroute?token=eyJ0eXAiOiJKV1QiLCJhbGci....
The problem is that I have an exception in JWT.php:
Tymon \ JWTAuth \ Exceptions \ JWTException
A token is required
JWT.php
protected function requireToken()
{
if (! $this->token) {
throw new JWTException('A token is required');
}
}
How can I decode the token that is passed as URL parameter and not in the header of the request?
If you are using jwt-auth dev, the toUser method in old version will drop the error like above, try this:
// Get the currently authenticated user
$user = auth()->user();
If the user is not then authenticated, then null will be returned.
Instead of
$user = JWTAuth::toUser($request->input('token'));
Use
$user = $this->jwt->User();
I solved it using this code:
use Namshi\JOSE\SimpleJWS;
$secret = config('jwt.secret');
$jws = SimpleJWS::load($token);
if (!$jws->isValid($secret)) {
return response()->json([], 401); // unauthorized
}
$payload = $jws->getPayload();
$account = Account::find($payload["account"]["id"]);
However I would have preferred to use directly JWTAuth

Laravel and jwt-auth - how to check if the user is logged in

I have set up Laravel with jwt (using jwt-auth). In my Kernel.php - $routeMiddleware I have added :
'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class
As I understand it 'jwt.refresh' will automatically refresh / create a new token for the user for each request.
In my single page ajax app I need to check if the user is logged in so I have added a route that calls this function:
public function isAuthenticated() {
$token = JWTAuth::getToken();
if(!$token){
throw new JWTException('Token not provided');
}
try{
$token = JWTAuth::refresh($token);
}catch(TokenInvalidException $e){
throw new AccessDeniedHttpException('The token is invalid');
}
return $this->response->withArray(['token'=>$token]);
}
The problem is that when isAuthenticated() is called the JWTAuth::refresh($token) call fails.
I guess it has something to do with that the token is refreshed.
What I want to do is to return true if the client's token is valid.
Is there a way to do this?
Removing 'jwt-refresh' seems to not solve the issue for us.
Thank you in advance!
My first observation is, where is the token stored? Is it parsed with the request? Because I believe that if your app uses jwt with api, then each request should have a token to signify a logged in user so something like this would be helpful:
try {
if (! $token = JWTAuth::parseToken()) {
//throw an exception
}
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
//throw an exception
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
//throw an exception
} else if ( $e instanceof \Tymon\JWTAuth\Exceptions\JWTException) {
//throw an exception
}else{
//throw an exception
}
}
If successfully parsed from the request, then:
$user = JWTAuth::toUser($token);
see: https://github.com/tymondesigns/jwt-auth/wiki/Authentication
With your example code, if the token is not set - nothing is retrieved. However, if you want session based authentication, why not use the default authentication from Laravel.
Hope this helps :)
You can get the current user related to token and check if not null:
$user = JWTAuth::setToken($token)->toUser();
if($user == null){
abort(401);
}

Laravel issue with loginUsingId (Manual Authentication)

I am trying to implement a single signon on multiple domains. The concept is pretty simple i.e to send unique user tokens and then verify these tokens to find the user and then log him in.
Now after verifying the token and then grabbing the user, i do something like this
$loggedInUser = Auth::loginUsingId($user->id, true);
Now i have a custom middleware where it first checks for a logged in user, i.e
Auth::Check()
The above works fine for the first time. But on refresh Auth::check() is not validated. I have also tried using all different session drivers but still doesn't work.
I used a similar code on laravel 5.2, and it did work. But on laravel 5.3 its not validating on persistent requests.
Edit: Let me show you my Code
I have not modified AuthServiceProvider or any other guard. I do have the user model inside a directory but i have modified the path in auth.php.
Here is the route that domain1 points to:
http://domain2.com/{{$role}}/{{$route}}/singlesignon/{{$token}}
This is then picked up by verifySingleSignOn method inside the loginController which takes in the role, route that the user came in from other domain and the token. The user is then redirected to the same routes, but on domain2. Here i can successfully recieve the user id before manually logging in.
public function verifySingleSignOn($role, $route, $token)
{
// Fetch Single Signon
$userRepository = new UserRepository();
$user = $userRepository->checkForSingleSignOnToken($token, ['id']);
// Check if Token Exists
if (isset($user->id) && is_int($user->id) && $user->id != 0) {
// Manually Logging a user (Here is successfully recieve the user id)
$loggedInUser = Auth::loginUsingId($user->id);
if (!$loggedInUser) {
// If User not logged in, then Throw exception
throw new Exception('Single SignOn: User Cannot be Signed In');
}
$redirectTo = $role . '/' . $route;
return redirect($redirectTo);
} else {
return Auth::logout();
}
}
Then i have this GlobalAdminAuth middleware
// Check if logged in
if( Auth::Check() ){
$user = Auth::User();
// Check if user is active and is a globaladmin
if( !$user->isGlobalAdmin() || !$user->isActive() ){
return redirect()->guest('login');
}
}else{
return redirect()->guest('login');
}
return $next($request);
Now the first time everything works fine and the user moves through the middleware successfully . but the second time the else statement is triggered.
Edit: Code for checkForSingleSignOnToken
public function checkForSingleSignOnToken($token, $columns = array('*'))
{
return User::where('single_signon', $token)->first($columns);
}
try
Auth::login($user);
instead of
Auth::loginUsingId($user->id, true);
Cookies are restricted domain-wise. Your application on domain1.com wont be able to grab cookies set by domain2.com.
You should be customizing the guard to use some other mechanism than cookies. Maybe use a token in the query parameters.
add this to your protected $middleware array in app\Http\Kernel.php
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class
I think it has to do with an update in the framework
no using auth:check in middleware
using request->user() or auth::user()
Please review bellow code structure, i had made manual authentication
in laravel 5.0.
routes.php
Route::get('login_user_by_id/{id?}', ['as' => 'login_user_by_id', 'uses' => 'UsersController#login_user_by_id']);
Route::post('user_login_post_for_admin',['as'=>'user_login_post_for_admin','uses'=>'LoginController#user_login_post_for_admin']);
Route::get('user_logout', ['as' => 'user_logout', 'uses' => 'UsersController#user_logout']);
LoginController.php
public function user_login_post_for_admin(){
$this->set_email($_POST['email']);
$this->set_password($_POST['password']);
$this->set_login_requested_role(['Admin','Moderator']);
return $this->user_login_post();
}
public function user_login_post(){
$User = new User();
if(isset($this->email) && !empty($this->email)){
$User->set_email(trim($this->email));
$User->set_password(Hash::make(trim($this->password)));
$user_login_data = $User->check_email_password_for_login();
if(isset($user_login_data) && !empty($user_login_data)){
if (Hash::check(trim($this->password), $user_login_data[0]->password)) {
$response['user_id']=$user_login_data[0]->id;
$response['name']=$user_login_data[0]->name;
$response['surname']=$user_login_data[0]->surname;
$response['profile_picture']=$user_login_data[0]->profile_picture;
$response['SUCCESS']='True';
$response['MESSAGE']='Login Success.';
return Redirect::route('login_user_by_id',[$user_login_data[0]->id]);
}else{
Session::put('SUCCESS','FALSE');
Session::put('MESSAGE', 'Invalid Credential.');
return redirect()->back();
}
}else{
Session::put('SUCCESS','FALSE');
Session::put('MESSAGE', 'Invalid Credential.');
return redirect()->back();
}
}else{
Session::put('SUCCESS','FALSE');
Session::put('MESSAGE', 'Invalid Credential.');
return redirect()->back();
}
}
UsersController.php
public function login_user_by_id($id=''){
if(isset($_GET['id'])&&!empty($_GET['id'])){
$id = $_GET['id'];
}
$User = new User();
$Log=new Log();
$user_for_auth = $User->find($id);
Auth::login($user_for_auth, true);
$User->id=AUTH::user()->id;
$auth_user_role=$User->auth_user_role();
$rl_title=$auth_user_role[0]->rl_title;
return Redirect::route('admin_home');
}
public function user_logout(User $user){
$User=new User();
$login_user_id = AUTH::user()->id;
$User->id=AUTH::user()->id;
$auth_user_role=$User->auth_user_role();
$login_user_role=$auth_user_role[0]->rl_title;
$response['user_id']=$login_user_id;
$response['SUCCESS']='TRUE';
$response['MESSAGE']='Successfully Logout.';
Auth::logout();
return Redirect::route('admin_login');
}

Resources