Laravel JWT multi-page structure

As far as I know, a JWT-based authorization system is usually reserved for SPA (you know, one view, one React / Angular / Vue application, with one bloated app.js file), however I am trying to use JWT magic with a slightly separate structured application.

Structure

Instead of showing only one blade.php view from my Laravel application, which displays one application and a Vue instance, I try to run two separate blade.php , each of which acts as their own separate Vue SPA : one for the appearance of the application (pre-auth), and the other for the inside of the application (post-authorization).

Current state of application

To connect the application authentication system, I used Tymon jwt-auth lib (beautiful btw lib) and bundled everything together on the front (as mentioned earlier) of Vue/Vuex . Everything works as expected, in my Register and Login components I can hit my api, get the JWT in response, save it locally, and then add the specified token to Axios , allowing all subsequent requests to use this token.

Dilemma

Now I'm at a crossroads. The post-out-route / view that I want to serve is protected by special JWT middleware that redirects if a valid token is not provided:

 Route::get('/home', 'Auth\ HomeController@home ')->middleware('jwt'); 

intermediate layer

 class JWT { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { JWTAuth::parseToken()->authenticate(); return $next($request); } } 

and my preview and all of its routes are protected by the native guest word RedirectIfAuthenticated Laravel, which is now protected by JWT:

 class RedirectIfAuthenticated { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null $guard * @return mixed */ public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { return redirect('/home'); } return $next($request); } } 

Questions

So this leads to the following questions:

1) after successful registration / login to the interface, a JWT is created, stored locally and in the Axios headers. How do I redirect to my post-out route with this valid token?

2) How can I make sure that Valid JWT is saved and present when guest routes are deleted in order to successfully redirect back to my post-out route?

I would prefer to keep all the redirects and checks for the backend, if possible

+5
source share
3 answers

So here is the logic I ended up:

In my LoginController.php login LoginController.php after successful authentication and JWT generation, I return a response with Json and a new cookie, as with the new token passed in:

 public function login(Request $request) { $creds = $request->only(['email', 'password']); if (!$token = auth()->attempt($creds)) { return response()->json([ 'errors' => [ 'root' => 'Incorrect Credentials. Try again' ] ], 401); } return $this->respondWithToken($token); } protected function respondWithToken($token) { return response()->json([ 'meta' => [ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth()->factory()->getTTL() * 60 ] ], 200) ->withCookie(cookie('access_token', $token, auth()->factory()->getTTL())); } 

In my guest check, RedirectIfAuthenticated for cookie, if there is, setToken , which in turn sets Guard to Authenticated and will always be redirected to /home if the token is available and valid:

 public function handle($request, Closure $next, $guard = null) { if ($request->hasCookie('access_token')) { Auth::setToken($request->cookie('access_token')); } if (Auth::guard($guard)->check()) { return redirect('/home'); } return $next($request); } 

And in my post-auth Routes setToken , I also setToken , and if it is valid and exists, it will allow access, otherwise the number of JWT errors that are simply redirected to the preview will be selected:

 public function handle($request, Closure $next) { JWTAuth::setToken($request->cookie('access_token'))->authenticate(); return $next($request); } 

Finally, I decided to handle the redirection in the interface when I use Axios, which will promise based on, and can assure that the cookie will be set before being redirected to post-auto-view, so no funny thing will happen! Hooray! Hope this helps someone in their quest for the magic of Multi-page SPA!

0
source

So you can make sure that the JWT token is available everywhere for Axios or even any external interface.

The most common way is to store the token in a cookie or in the browser web storage ( localStorage / sessionStorage )

The difference between localStorage and sessionStorage is that the data stored in localStorage is saved through browser sessions, sessionStorage is cleared when the page session ends.

The general consensus is that cookies are a bit more secure as they have a smaller attack vector, although none of them are completely secure. If you want to go deeper, you can start by reading this article.

To get more specific information about your problem, first you want to set up a marker store using one of the methods described above, the recommended method is cookies, you can find examples of how to do this using pure Javascript.

Now that you have a marker on each page, you can redirect the user depending on what you like. Although I would suggest that instead of using your own JWT authentication middleware, you can use the one provided by the JWT library: jwt.auth .

This middleware will automatically respond with error codes, if something is wrong with the token, if any, it will return one of the following HTTP responses:

  • token_not_provided
  • token_expired
  • token_invalid
  • user_not_found

If one of these answers is returned (or if the request status code is 400), you can simply use the interface to redirect the user back to your pre-authorization routes.

When logging in after saving a cookie in a cookie, use the interface to redirect to post-out routes.

I know that you said that you want to keep the redirection logic in the backend, but it does not make sense when, for example, you call the API when you log in, you cannot return the marker, they cause redirection at the same time as the backend.

UPDATE

A very simple example of how you can only authenticate with security and still get the token for the API. Borrowing from the redirect example from @Ohgodwhy, you can put the following into your RedirectIfAuthenticated middleware.

 public function handle($request, Closure $next, $guard = null) if (Auth::guard($guard)->check()) { if ((\Cookie::get('access_token') == null)) { $cookie = \Cookie::make( 'access_token', \JWTAuth::fromUser(Auth::user()), config('session.lifetime'), null, $request->refeerer, false, // to make the cookie available in javascript false // to make the cookie available in javascript ); return redirect('/home')->cookie($cookie); } else { return redirect('/home'); } } return $next($request); } 

Just make sure your $redirectTo in app/Http/Controllers/Auth/LoginController.php set to a path that implements the RedirectIfAuthenticated middleware.

0
source

Upon successful login, you will have a token, let it be called $jwt_token

You can redirect to the protected page after authorization and set a cookie in response:

 return redirect('/home')->cookie( 'access_token', //name $jwt_token, //value config('session.lifetime'), //expiration in minutes (matches laravel) config('app.url'), // your app url true // HttpsOnly ); 

From here, Axios can access the cookie by analyzing the cookies in the document and extracting the access_token

 let token = document.cookie.split(';') // get all your cookies .find(cookie => cookie.includes('access_token')) // take only the one that matches our access_token name .split('=')[1] // get just the value after = // terrible code example above for you 

Now you can use this in your Axios requests by adding it as a Bearer value to the Authorization header:

 Authorization: `Bearer ${token}` 

Your JWT middleware already uses the authenticate method, and so it must handle expiry for it:

 JWTAuth::parseToken()->authenticate(); 

Under the hood, this will try to check the token expiration based on the current TTL set in the config/jwt.php . Given your workflow, I would also have a blacklist token if it expires. You can add an Event Listener that listens to expired tokens and blacklist them by listening to Event::listen('tymon.jwt.expired'); .

I apologize for any syntax errors, formatting problems or spelling errors, I am on my pone and will edit it later to solve them.

0
source

Source: https://habr.com/ru/post/1276021/


All Articles