Laravel 5.3 API route not saving session between requests - laravel

I am trying to build a static HTML viewer through Laravel's 5.3 API routing logic and JWT. The files are all stored on S3 and need to be protected so I thought the best way to do this was to make a kind of proxy that all the files pass through. That way I can check the token of the user from the API request and load the files accordingly.
The first file loads fine.
http://example.com/api/proxy/file.html?token={token}
The issue arises when the HTML file tries to load files from itself. It works when I strip out the authentication functions so I know it's not an issue with getting the files. It's because the token is not appended to future requests. It sends this instead without the token.
http://example.com/api/proxy/some_image.png
I attempted to add the following code to my token checker logic.
public function __construct(JWTAuth $jwtAuth)
{
$this->middleware(function ($request, $next) use ($jwtAuth) {
if (!$jwtAuth->getToken()) {
if (!Auth::user()) {
return response()->error('The token could not be parsed from the request', 400);
} else {
$this->authUser = Auth::user();
}
} else {
$this->authUser = $jwtAuth->parseToken()->authenticate();
Auth::setUser($this->authUser);
}
return $next($request);
});
}
But for some reason this does not work. When the first .html loads up with the token it tries to authenticate the user using Laravel's Auth middleware but Auth::user() returns null on the image request.

Related

How to authenticate a user by its user id in Laravel in a custom middleware

How would you guys go about implementing something like this? I use Laravel as an API only. We have other framework where the login was implemented where it saves an httpOnly cookie (sessionId) after the user logs in. That's the main framework. We're migrating away from that old framework (Zend).
With the sessionId sent to Laravel from, say, a JS frontend, I'm able to lookup the current user based on the sessionId. That sessionId is then used to query the session database. I've created a middleware called "CheckForCurrentUser.php":
[..]
public function handle(Request $request, Closure $next)
{
// The reason for this is that the OPTIONS (request) does not include the cookie in the request.
$method = $request->method();
// SESSIONID is the name of the cookie created from the main framework
// once a user is logged in.
// SESSIONID is an exception in EncryptCookies.php
$sessionId = request()->cookie('SESSIONID');
if ($method === 'POST' && $sessionId !== '') {
// This function is only to get the logged in user id from the session database
$userId = $this->notImportantFunction($sessionId);
if ($userId) {
// User id found so make current user for this Laravel API
Auth::loginUsingId($userId);
} else {
// Instructs the frontend to let user log back in.
return abort(401);
}
}
return $next($request);
}
To recap, a user cannot authenticate from this Laravel application nor can I send an authentication token. They logged in from another framework. Laravel has access to the main framework databases.
This setup works. Using use Illuminate\Support\Facades\Auth; I use that as the "current user" in any controller/model.
Kernel.php looks something like:
[..]
protected $middleware = [
[..]
\App\Http\Middleware\CheckCurrentUser::class,
[..]
];
Before I go any further, is that how you'd implement something like this? It does work (ish) but I do not get the SESSIONID unless I check for a POST request.
I do not like this setup. I'm now using lighthouse and having issues using both the #auth and #inject directives. This is due to how I authenticate a use with Laravel so sorting how I authenticate a user should sort Lighthouse. Any tips on how to refactor this the right way? My routes are with /api/some-string
Lighthouse is not the issue. Should I send a authentication header, Lighthouse works. I believe Laravel does something behind the scenes with it sees a token in the header. I cannot send an authentication. I can only rely on the cookie. It's httpOnly so I have no access to that from JavaScript.

how to check if user is authenticated with passport (get user from token using laravel-passport)

I am using Passport to log in users to a Laravel API endpoint, users get authenticated using their social accounts (google, facebook) using laravel-socialite package.
the workflow of logging users in and out works perfectly (generating tokens...Etc). The problem is I have a controller that should return data based on whether there is a user logged in or not.
I do intercept the Bearer token from the HTTP request but I couldn't get the user using the token (I would use DB facade to select the user based on the token but I am actually looking whether there is a more clean way already implemented in Passport)
I also don't want to use auth:api middleware as the controller should work and return data even if no user is logged in.
this is the api route:
Route::get("/articles/{tag?}", "ArticleController#get_tagged");
this is the logic I want the controller to have
public function get_tagged($tag = "", Request $request)
{
if ($request->header("Authorization"))
// return data related to the user
else
// return general data
}
Assuming that you set your api guard to passport, you can simply call if (Auth::guard('api')->check()) to check for an authenticated user:
public function get_tagged($tag = "", Request $request)
{
if (Auth::guard('api')->check()) {
// Here you have access to $request->user() method that
// contains the model of the currently authenticated user.
//
// Note that this method should only work if you call it
// after an Auth::check(), because the user is set in the
// request object by the auth component after a successful
// authentication check/retrival
return response()->json($request->user());
}
// alternative method
if (($user = Auth::user()) !== null) {
// Here you have your authenticated user model
return response()->json($user);
}
// return general data
return response('Unauthenticated user');
}
This would trigger the Laravel authentication checks in the same way as auth:api guard, but won't redirect the user away. In fact, the redirection is done by the Authenticate middleware (stored in vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php) upon the failure of the authentication checking.
Beware that if you don't specify the guard to use, Laravel will use the default guard setting in the config/auth.php file (usually set to web on a fresh Laravel installation).
If you prefer to stick with the Auth facade/class you can as well use Auth::guard('api')->user() instead or the request object.
thanks to #mdexp answer
In my case I can resolve my problem with using
if (Auth::guard('api')->check()) {
$user = Auth::guard('api')->user();
}
In my controller.

Using laravel socialite and jwt-auth without session

Short version: What would be the appropriate way to send the JWT generated from Facebook login (laravel/socialite) to the angularjs front end without using session.
Long Version
I am making an app that has angularjs front end and laravel 5.2 backend. I am using tymondesigns/jwt-auth for authentication instead of session.
I am also using laravel/socialite for social Facebook authentication. For that I am using the stateless feature of socialite so that I don't need session in any ways.
The basic authentication works perfectly. But, when I try to use Facebook login, I follow these steps
User clicks on a button on the angular side that redirects to the provider login page of the back end.
public function redirectToProvider() {
return Socialite::with('facebook')->stateless()->redirect();
}
2. User gives his login information. After logging in he is redirected to my handlecallback function.
try {
$provider = Socialite::with('facebook');
if ($request->has('code')) {
$user = $provider->stateless()->user();
}
} catch (Exception $e) {
return redirect('auth/facebook');
}
return $this->findOrCreateUser($user);
Next I use the findorcreate function to determine whether the user exists or not. If not than I just create a new user and create JWT from that.
$user = User::where('social_id', '=', $facebookUser->id)->first();
if (is_object($user)) {
$token = JWTAuth::fromUser($user);
return redirect()->to('http://localhost:9000/#/profile?' . 'token=' . $token);#angular
} else {
$result = array();
$result['name'] = $facebookUser->user['first_name']
$result['email'] = $facebookUser->user['email'];
$result['social_id'] = $facebookUser->id;
$result['avatar'] = $facebookUser->avatar;
$result['gender'] = $facebookUser->user['gender'];
$result['status'] = 'active';
$result['login_type'] = 'facebook';
$result['user_type'] = 'free_user';
try {
$user = User::create($result);
} catch (Exception $e) {
return response()->json(['error' => 'User already exists.'], HttpResponse::HTTP_CONFLICT);
}
$token = JWTAuth::fromUser($user);
return redirect()->to('http://localhost:9000/#/profile?' . 'token=' . $token);#angular
}
My problem is, in the last block of code I am having to send the jwt to my frontend via url. Which isn't secure at all. What would be the right way to send the generated JWT to the frontend without using session. Thank you
The official documentation of Laravel Socialite says:
Stateless Authentication
The stateless method may be used to disable session state verification. This is useful when adding social authentication to an API:
return Socialite::driver('google')->stateless()->user();
Then, you can authenticate using the jwt-auth method:
JWTAuth::fromUser($user)
If you're using $http on the Angular side, try returning the token as a JSON response from Laravel:
return response()->json(compact('token'));
Then store the token in localStorage or sessionStorage or what have you.
If you're generating your Angular page from within Laravel (i.e. not using Laravel as an API, but showing your Angular page from /public/index.php, for instance) you could load the view with the token in the data for the view.
As long as you're using HTTPS either of these two scenarios are better than passing the token in the redirect URL.
You can store token and use client side redirect without storing to browser history to redirect user to profile page without token in URL:
document.location.replace({profile-url})

Laravel middleware one time authorization for route groups

I am designing some part of system in Laravel 5. It is expected to behavior as described below.
User gets unique url. It could be provided in email, but that will not matter.
He clicks it, and gets logged in with some temporary token (for a session lifetime), that gives him possibility to access all the urls in allowed route group, ex. account/*, but if he wants to reach other restricted urls, then he is asked to authorize with his username/password.
If he is already authorized, token login makes no effect for him.
My question is about possibility to do something like that in Laravel out of box. I know there are some middleware services, but I'm not sure if default Guard behavior will not need to be changed to work as I expect.
I used to work with Symfony before, and there it is solved by firewalls by default, so maybe also in Laravel there is prebuilt solution?
you can absolutely doing this use laravel, here is an example code not tested,
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if (preg_match('account', $request->route()->getName()) { //if url is under account, you can get route info from $request->route();
if (!session()->get($token)) { // if not have valid token
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->route('admin.login.index',['referrer'=>urlencode($request->url())]);
}
}
} else {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->route('admin.login.index',['referrer'=>urlencode($request->url())]);
}
}
}
return $next($request);
}
then from your route just add middleware auth to your group, this is a way to define you request in on middleware, laravel 5.2 support mutiple middleware.

Can I store an access Cookie in a Laravel session?

I am working with a remote API that is normally accessed directly via JavaScript. In the normal flow, The user authenticates by sending Auth headers and in return is granted a cookie.
What I am trying to do is send auth headers from a laravel app, authenticate in the app controller, and provide API access through laravel controller functions.
I was hoping this would be as simple as authenticating and sending my subsequent API calls, hoping that the cookie given to the PHP server would continue to grant authentication.
Well that doesn't work and thats fine, but now I am thinking that I need to store my access cookie in the Session, and send it in the headers for future API calls.
Will this work/how can I go about this? My supervisors don't want to implement OAuth type tokens on the remote server and to me that seems like the best route, so I am a bit stuck.
Cookies cannot be shared across multiple hosts. The cookie (on the client) is only valid for path which set it.
EDIT - ADDING ADDITION AUTH DETAIL
Setting up remember me in Laravel
When migrating (creating) you User table add $table->rememberToken()
to create that column in your User table.
When user signs up to your service add a check box to allow them to
make the decision OR you can just set it true if you don’t to offer
the user the option as described in step 3
< input type="checkbox" name="remember" >
In your controller you add the following code:
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// The user is being remembered...
}
Users table must include the string remember_token column per 1. , now assuming you have added the token column to your User table you can pass a boolean value as the second argument to the attempt method, which will keep the user authenticated indefinitely, or until they manually logout. i.e. Auth::attempt([$creditentials], true);
Side note: the Illuminate\Contracts\Auth\UserProvider contract, public function updateRememberToken(Authenticatable $user, $token) uses the user’s UID and token stored in the User table to store the session auth.
AUTH ONCE:
Laravel has once method to log a user into the application for a single request. No sessions or cookies. Used with stateless API.
if (Auth::once($credentials)) {
//
}
OTHER NOTES
The remember cookie doesn't get unset automatically when user logs out. However using the cookie as I explained below in cookies example you could add this to your logout function in your controller just before you return the redirect response after logout.
public function logout() {
// your logout code e.g. notfications, DB updates, etc
// Get remember_me cookie name
$rememberCookie = Auth::getRecallerName();
// Forget the cookie
$forgetCookie = Cookie::forget($rememberCookie);
// return response (in the case of json / JS) or redirect below will work
return Redirect::to('/')->withCookie($forgetCookie);
OR you could q$ueue it up for later if you are elsewhere and cannot return a response immediately
Cookie::queue(forgetCookie);
}
Basic general cookie example that might help you. There are better approaches to do this using a Laravel Service provider
// cookie key
private $myCookieKey = 'myAppCookie';
// example of cookie value but can be any string
private $cookieValue = 'myCompany';
// inside of a controller or a protected abstract class in Controller,
// or setup in a service ... etc.
protected function cookieExample(Request $request)
{
// return true if cookie key
if ($request->has($this->myCookieKey)) {
$valueInsideOfCookie = Cookie::get($this->myCookieKey);
// do something with $valueInsideOfCookie
} else {
// queue a cookie with the next response
Cookie::queue($this->myCookieKey, $this->cookieValue);
}
}
public function exampleControllerFunction(Request $request)
{
$this->cookieExample($request);
// rest of function one code
}
public function secondControllerFunction(Request $request)
{
$this->cookieExample($request);
// rest of function two code
}

Resources