Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login with JWT and Web #1669

Open
homeoftheunits opened this issue Sep 21, 2018 · 30 comments
Open

Login with JWT and Web #1669

homeoftheunits opened this issue Sep 21, 2018 · 30 comments

Comments

@homeoftheunits
Copy link

Hi there,

until now i use jwt to login in my api. But now i want to use Horizon additionally. To secure the horizon app, i want to use the laravel LoginController. The Login succes, but the auth()->user() is everytime null. Mayby its an conflict with JWT, what can i do?

Cheers
Ralf

@shirshak55
Copy link

you can use middleware web etc for that .And use auth:web middleware for authentication. For part you need jwt use api and auth:api

@LastxTemplar
Copy link

@shirshak55 would you be able to provide an example of this? I am trying to achieve the same thing for 2 days now and I can't. For me it all depends on what I put in the /config/auth.php file. if defaults->guard => api then my api login routes work but the web ones dont, and it's vice-versa if I put defaults->guard => web

Thanks!

@shirshak55
Copy link

shirshak55 commented Oct 16, 2018

@LastxTemplar
hmm have you set auth:api and api middleware for api related routes?

the best option is to put web by default and use middleware api explicitly :)

@LastxTemplar
Copy link

hey @shirshak55
thanks for the response. That is exactly what I wanted to do. Basically I have a fresh Laravel 5.7 installation, and I use "tymon/jwt-auth": "^1.0.0.rc3" for the jwt package.
Here is my config/auth.php:

[...]
'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
[...]
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],

Here is my routes/api.php

Route::group(['middleware' => ['api']], function () {
	// User
	Route::post('/user/login', ['as'=>'api.user.login', 'uses' => 'ApiAuthController@login']);
	Route::post('/user/register', ['as'=>'api.user.register', 'uses' => 'ApiAuthController@register']);
});

And here is my ApiAuthController.php

class ApiAuthController extends Controller
{

	public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

    public function register(Request $request) {
        $validator = Validator::make($request->all(), [
            'name'          => 'required|string|min:3|max:255',
            'phone_number'  => 'nullable|numeric|digits_between:4,20',
            'email'         => 'required|email|max:255|unique:users,email',
            'password'      => 'required|string|min:6|confirmed',
        ]);

        if ($validator->fails()) {
            return response()->json(['validation_errors' => $validator->errors()], 401);
        }
        
        $user 				= new User();
        $user->name 		= $request->name;
        $user->email 		= $request->email;
        $user->phone_number	= $request->phone_number;
        $user->password 	= Hash::make($request->password);
        $user->save();

        $token = NULL;
        $credentials = request(['email', 'password']);
        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return response()->json(['message' => "Successfully Registered!", 'user' => $user, 'token' => $token], 200);
    }

    public function login()
    {
        $credentials = request(['email', 'password']);

        $token = NULL;
        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        $user = auth()->user();
        $token = auth()->tokenById($user->id);

        return response()->json(['user' => $user, 'token' => $token], 200);
    }

I used php artisan make:auth after I made the fresh Laravel installation so I haven't touched any of the Auth Controllers that Laravel created.

With this current setup I am now able to login from the webpage but when trying to login through the API by posting to http://xxx.local:8888/api/user/login I get this error:
"message": "Method Illuminate\\Auth\\SessionGuard::tokenById does not exist."

Now, if I change my config/auth.php to this:

[...]
'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],

I can now login successfully through the API but if I go to my browser and try to login, I just get redirected to the login page without actually being logged in.

It seems to me that the framework does not handle correctly the default values in the config/auth.php, at least that's my guess.

Thanks in advance for your help!

@shirshak55
Copy link

shirshak55 commented Oct 17, 2018

for api route use api guard bro.

auth('api')

Or

\Auth::guard('api')::attempt(...............)

Otherwise auth() will use web which don't work with api. I am using web route and jwt is working perfectly

Here is my sample authenticate controller

<?php

namespace Shirshak\User\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

    /**
     * Get a JWT token via given credentials.
     *
     * @param  \Illuminate\Http\Request  $request
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if ($token = $this->guard()->attempt($credentials)) {
            return $this->respondWithToken($token);
        }

        return response()->json(['error' => 'Invalid Login Details'], 401);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json($this->guard()->user(), 200);
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        $this->guard()->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken($this->guard()->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type'   => 'bearer',
            'expires_in'   => $this->guard()->factory()->getTTL() * 60,
        ]);
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\Guard
     */
    public function guard()
    {
        return Auth::guard('api');
    }
}

@LastxTemplar
Copy link

Oh wow, this actually worked! Thank you so much!
Although I don't understand this too well, admittedly I am new at using guards, but when we are setting
$this->middleware('auth:api', ['except' => ['login']]);
doesn't this mean that it should use this guard from the config/auth.php?

'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],

@shirshak55
Copy link

shirshak55 commented Oct 17, 2018

@LastxTemplar
Think middleware like onions. When user hit the router first thing is it passes from middleware.

$this->middleware('auth:api', ['except' => ['login']]); means the route which is hitting this controller requires authentication with driver api. If not then it throws invalid message.

auth('api') means use api based authentication manager so auth('api')->check() means using use api based which you have set

 'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],

If you dont use anything it uses default guard so auth() means auth('default_guard_name')
Its not that complex :)

@LastxTemplar
Copy link

Thanks, makes more sense now :)

@aomini
Copy link

aomini commented Feb 1, 2019

@shirshak55 @LastxTemplar I was stuck at this as welll... but now what happened is I have auth::api() in my middleware. If I pass token the it works fine ... but i have a route whose controller checks if user is authenticated() or not.. If I pass auth:api it will authenticate and I can check for auth() in my controller if I dont pass authorization it blocks it what should I do ?

@shirshak55
Copy link

shirshak55 commented Feb 1, 2019

@rakeshshubhu pass api middleware so you can use auth()->check(). i mean api not auth:api

@mratwan
Copy link

mratwan commented May 5, 2019

@shirshak55 would you be able to provide an example of this? I am trying to achieve the same thing for 2 days now and I can't. For me it all depends on what I put in the /config/auth.php file. if defaults->guard => api then my api login routes work but the web ones dont, and it's vice-versa if I put defaults->guard => web

Thanks!

Hi, My Friend
When you using "default" guard for "web" and you want to user "api" for api routes, in ApiAuthController, you must use auth('api) to get true result!

@shirshak55
Copy link

@mratwan Yes saying same if u use default guard as jwt u can use auth()->check().

But most people use web as guard so I recomment using auth()->guard('api') Or auth('api')

By the way we will need to set middleware to api otherwise we may get unexpected result.

@athallarizky
Copy link

Hi @shirshak55 ! Do you know about error Method Illuminate\Auth\RequestGuard::attempt does not exist ? I think it's related with this thread, I cannot access that method, do you have any solution? I already read it in other thread but no one can fix it. Thanks!

@shirshak55
Copy link

@athallara check ur config auth file and what is the value?

@athallarizky
Copy link

@shirshak55 Here it is..

`<?php
return[
'defaults' => [
'guard' => env("AUTH_GUARD", "api"),
'passwords' => 'users',
],
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],

]
?>`

@shirshak55
Copy link

it looks good. did u use middleware: api?

@athallarizky
Copy link

@shirshak yeah sir, I already do that :(

@shirshak55
Copy link

@athallara hmm can u show me how is ur routes files?

@athallarizky
Copy link

@shirshak55 here sir, fyi i'ts rest api using lumen 5.8.6 .. is there any different configuration for it?

`$router->group([
'prefix' => 'v1',
'middleware' => 'api' //if this commented still doesn't work
], function () use ($router){

//Authentication
$router->post('register', 'Auth\AuthController@register');
$router->post('login', 'Auth\AuthController@login');

});`

@shirshak55
Copy link

do composer dump-autoload -o.

'middleware' => 'api' //if this commented still doesn't work

don't comment that though if u are using api route

And auth()->guard('api')->attempt should work

@athallarizky
Copy link

@shirshak55 thanks sir! it's worked! I don't know why, I already do dump-autoload before but still doesn't work, but it's work this time! Thanks! :)

@shirshak55
Copy link

shirshak55 commented Oct 15, 2019 via email

@fingersandmind
Copy link

@shirshak55 I'm still having issues even if I'm using the same as what example you have given. I'm getting error in my postman saying

Method Illuminate\Auth\SessionGuard::factory does not exist.

@fingersandmind
Copy link

My bad. I just missed to modify this part also

'expires_in' => $this->guard()->factory()->getTTL() * 60
//used auth() instead of the example above

@fingersandmind
Copy link

@shirshak55 Can you please explain to me sir, why both my login and register function works if excluded on middleware('auth:api')?

@shirshak55
Copy link

@fingersandmind sir are u using web.php file or api.php file inside routes? By default api uses api middleware and web uses web middleware . So horizon etc can only work on web middle as per as I know.

Regarding login u must only use api middleware and do have to configure guards etc properly so it wont collide sir.

@XenitXTD
Copy link

For anyone that wants to handle this better...

I used the following.

https://medium.com/@JinoAntony/multi-user-api-authentication-using-laravel-jwt-8ae572b0c4cf

It does a better job of this by creating a middleware that assigns a guard

This way you dont have to change your Auth config only add a provider and then use the assign guard middleware on the route to selectively assign the guard you want to use.

Do note the jwt.auth will fail a route that is not authenticated so only use the guard like so

Route::group(['middleware' => ['assign.guard:users']],function () {});

if you have a route that is authenticate with a token you can add on that jwt.auth.

This resolves the issue of needing to add the api as default allowing you to use the web frontend and api without issue.

@khessels
Copy link

@stale
Copy link

stale bot commented Dec 25, 2020

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the stale label Dec 25, 2020
@ipekelaytac
Copy link

i have a website and i want to do security with jwt how can i do this with web.php without using api

@stale stale bot removed the stale label Dec 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants