Laravel combine Passport authentication and normal authentication - laravel

How do I combine Passport authentication and normal laravel authentication?
I want the user to be logged in on pages of web-middleware and api-middleware. The login route is in api-middleware. Currently I have set up Passport authentication and it works fine for all api-middleware routes. How to make the user logged in in web-middleware as well?
Edit #1
What Im doing:
Login code
$http = new \GuzzleHttp\Client();
try {
$response = $http->post(config('services.passport.login_endpoint'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('services.passport.client_id'),
'client_secret' => config('services.passport.client_secret'),
'username' => $args['email'],
'password' => $args['password']
]
]);
$user = User::where('email', $args['email'])->first();
Auth::guard('web')->login($user);
return [
"token" => $response->getBody()->getContents(),
"user" => $user
];
} // ...
Somewhere in some web-middleware route
return auth()->check() ? "logged in" : "not logged in";
returns "not logged in"

Ideally you shouldn't, as passport auth is for a separate app communicating to the API and laravel preshipped auth is for MVC, they are separate user sessions.
But assuming you know what you are doing, either call Auth::login($user); on user login via API, or generate the passport token when the user login through web middleware auth, whichever login happens first...
Remember Auth::login($user); creates a user session and sets cookies to refer to that session... So you create for yourself a new problem were on logout, there are two places to logout from... as technically the user is logged in twice, with passport token and with a cookie referring to his session...

Actually I'm in a situation like you were. I have searched a lot about it. I always needed web authentication because of nature of my projects but in addition I started to develop projects with api backend soo late in comparing with web development world.
I'm a bit lazy so I generally use Laravel Passport and without working always out of the box, it does the job so in my opinion if you want just the functionality of access tokens for api security, put your user login authentication on web side and just authenticate the api endpoints with auth:api middleware in your api.php file.
I know that that's not the best practice but since it sounds that you are not developing a pure Laravel SPA then you can follow the route for Laravel Multipage application with Vue powered.
But believe me best way is to use either web authentication or api authentication not together then as the above answer says, you will have two authentication working at the same time which does not sound ok.
At the end, when you want to get the authenticated user on blade templates you will use
auth()->user()
but on the other hand in api controllers you should use
auth('api')->user()
which is nice to have but dangerouse to use.

If you need to log an existing user instance into your application, you may call the login method with the user instance.
Auth::login($user);
You can also use the guard() method:
Auth::guard('web')->login($user);
See the documentation here for more information: https://laravel.com/docs/5.8/authentication#authenticating-users

Related

Laravel Jetstream/Sanctum API authentication

I have been working with Laravel since version 5.X up to version 8.X but always use it for backend API (never used blade template), and always pair it with VueJS on the front-end using JWT authentication (also never messed with any other authentication method).
Now with Laravel 9 and Vue 3, Im trying to use native Laravel Jetstream that uses SANCTUM and Vue+Inertia JS, and I'm quite lost with the authentication process. with JWT method, once the user succesfully login on the browser, all api request to Laravel will be authenticated using Authoraziation header. but this seems a different case with Sanctum.
After deploying and installing Jetstream and completed all the set-up. I created a user and loggedin with that user details. and I notice few things, there is a default API route
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
when I tried to directly access my.domain/api/user I notice it was redirected to GET /login
then redirected again to GET /dashboard
I then created a test api route using below
Route::get('test', function( Request $req) {
dd( [
'test' => $req->all(),
'user' => auth()->user(),
'request' => $req
] );
});
and I notice this request is not authenticated even when the cookies is present on the request as Im when I'm alraedy logged-in on the same browser, the auth()->user() is null.
I tried adding auth:sanctum middleware
Route::middleware('auth:sanctum')->get('test', function( Request $req) {
dd( [
'test' => $req->all(),
'user' => auth()->user(),
'request' => $req
] );
});
but having sanctum middle behave the same as the api/user where if i open api/test directly on the browser, it gets redirected to GET /login then redirected again to GET /dashboard and I'm quite lost at this point. I tried reading the docs and it says I have to do a separate authentication for this that would issue an API token and I was thinking I might better be going back with using JWT auth as it seems a lot easier to deal with.
So my question is; How can I authenticate an API end-point without having to redirect it to /login then /dashboard if the user is already logged in on my application using default sanctum authentication.
My goal is just to simply create /api/test that will be automatically authenticated if user already loggedin on the same browser and return the data I set on its return value and not doing any redirects.
Appreciate any help
I have got the same issue with laravel8
Jetstream and inertia vue3.
Am looking for the solution since 3 days posting messages on discord, searching on YouTube and more but nothing.
When i make an api call from your SPA to laravel, i got UNAUTHENTICATED response.
on postman you need put
headers
Accept = application/json
this tells your application know how works with Json
and go stop redirect to "Login"

Laravel sanctum API, retrieve the token for use in view components

Here is the context. I have two sites using the same domain.
The first using Laravel and view components The second is an "API", I use Laravel Sanctum. This API has a single user. Later, there will be a third site using this same API as well.
The API authentication system works perfectly. When I switch from Postman my user, my token is returned.
I'm wondering about token retrieval and usage on my first site using vue components.
My idea was to store API user login credentials in my .env file and retrieve the token in controllers where I use vue components.
Another solution would be to fetch my token on each call to the API, but that could be cumbersome.
Finally store the token at the user's connection and reuse it but where to store it, in session, in cookies,... Security level is not ideal.
Thanks in advance for your ideas.
The default thing to do to use token as a bearer token from the front end is as following
login attempt to backend and you will get the token to authenticate your request later.
store it using vuex store in you user store
then do your API request using that token
destroy the token after logout
if you are concerning about security, well the token should have expiration time and the token itself should be destroyed from the backend when the user is logged out. Every time the user do the login, new token will be created for them
If your API authentication is working properly, Then you've to store your token on the database every time the user logs in. And delete on some other occasions. To store your token on database every time the user logs in use the following code:
public function login(Request $request)
{
if (!Auth::attempt($request->only('email', 'password'))) {
return response()->json([
'message' => 'Invalid credential'
], 401);
}
$user = User::where('email', $request['email'])->firstOrFail();
//store the hashed token on db and return plain text token for the user
$token = $user->createToken('token_name')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
}
I took the code from here

How to use Laravel and Nuxtjs with Authentication, including login, logout, and password resets?

I can't find any resource to help with this issue, there are some repos that provide some type of base, but not many of them actually work.
Goals: Run Laravel has the backend API, run NuxtJS as the frontend SPA, either 2 separate locations or combined into one.
Needs to have proper authentication between both systems for logging in users. Laravel Sanctum looks to solve some of the SPA issues, but its hard to find proper documentation that actually shows a fully setup example.
Tested this idea https://github.com/zondycz/laravel-nuxt-sanctum But has failed, doesnt work with npm, must use yarn, however, login errors, doesnt work out of box. Repo needs work.
This tutorial Secure authentication in Nuxt SPA with Laravel as back-end was very indepth, and looked promising, however, refresh tokens don't seem to work with the SPA side since the author developed it on static nuxt. Though I feel like it could be modified to work, I havent found the solution yet.
This template, Laranuxt looked very promising, though I've yet to try it, im not sure if they are regularly updating it at this point, which was previously built by Laravel Nuxt JS (abandoned project)
I was able to run the #2, while refresh tokens dont exactly work, I can still authenticate the user, but now the other issue is password resets, which I'm unable to properly setup through the nuxt form.
Has anyone found resources or solved this issue with communication between these frameworks? or am I going done a rabbit hole that seems to have no end in sight?
I guess another way could be saying, can you do a fully restful authentication system?
Hopefully this isn't too broad of a topic, looking for some guidance on this issue as its hard to find proper tutorials or documentation without writing too much core code myself.
It seems many people struggle to implement Sanctum for SPA authentication when splitting the front and back across separate domains, and the problem is usually CORS related. The Sanctum documentation is great, but assumes a knowledge of CORS (or assumes requests will be same-origin). I'll break down the setup as I see it, providing a little extra support where I feel the docs fall short. A long answer, but towards the end I will address your question which seems to focus specifically on authentication.
Taken from Sanctum documentation:
First, you should configure which domains your SPA will be making requests from.
Assuming your front-end app lives at https://www.my-awesome-app.io, what is the domain? What about http://localhost:3000? Domains map to IP addresses, not protocols or port numbers. So the domains in the given examples would be www.my-awesome-app.io and localhost. With that in mind, all you need to do at this stage is go to the sanctum.php file in your config directory and set the value of the 'stateful' key to match the domain your Laravel API will receive requests from. Although domain names by definition do not include port numbers, the Sanctum docs make it very clear this is also required if you're accessing via a URL that requires a specific port.
/config/sanctum.php
...
'stateful' => [
'localhost:3000',
],
or
'stateful' => [
'my-awesome-app.io',
],
.env files are useful here.
If you are having trouble authenticating with your application from an SPA that executes on a separate subdomain, you have likely misconfigured your CORS (Cross-Origin Resource Sharing) or session cookie settings.
Indeed. So what does a correct setup look like? Assuming a recent Laravel version using the fruitcake/laravel-cors package, you will have a cors.php file in your /config folder. The default looks like:
Default
/config/cors.php
...
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
We have some work to do here. First, the paths. At the moment, our Laravel API is set up to allow requests from any external origin only if they are trying to access the /api/ routes*. This can lead to trouble early on when, as the Sanctum docs require, we try to access a csrf cookie from the path /sanctum/csrf-cookie. Requests to this path are not explicitly permitted in our cors.php file, so they will fail. To fix, we could do this:
'paths' => [
'api/*',
'sanctum/csrf-cookie'
]
and now requests to /sanctum/csrf-cookie will be permitted. As a sidenote, I find it personally very useful to change the prefix from sanctum to api, that way I can set a single base url for my http client (usually axios).
import axios from 'axios';
axios.defaults.withCredentials = true;
axios.defaults.baseURL = 'http://localhost:3000/api';
To change the path, you can change the following in the /config/sanctum.php file:
'prefix' => 'api',
Now GET requests to /api/csrf-cookie will return the cookie, instead of /sanctum/csrf-cookie.
Next, the allowed-origins. By default, it is set to *, which means "any origin". An origin is the protocol, domain and port number of the app sending a request to your Laravel API. So going back to our earlier examples, their origins would be http://localhost:3000 and https://www.my-awesome-app.io. These are the exact values you should use to allow requests from your front-end app:
'allowed_origins' => ['http://localhost:3000'],
I would recommend moving this to the .env file, and having a separate origin for local and production.
/config/cors.php
...
'allowed_origins' => [env('ALLOWED_ORIGINS')],
/.env
...
ALLOWED_ORIGINS=http://localhost:3000
The documentation does mention the last part of our cors config, which is that
'supports_credentials' => false,
Must be changed to:
'supports_credentials' => true,
Our /config/cors.php file now looks like:
Modified
/config/cors.php
...
'paths' => [
'api/*',
'sanctum/csrf-cookie'
],
'allowed_methods' => ['*'],
'allowed_origins' => [env('ALLOWED_ORIGINS')],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
Bonus info, Chrome will not allow a credentialed request to a server that returns the header
Access-Control-Allow-Origin: *
Google Chrome: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true
So you should make sure you set a specific origin in your cors config!
Finally, you should ensure your application's session cookie domain configuration supports any subdomain of your root domain. You may do this by prefixing the domain with a leading . within your session configuration file:
This isn't complicated, but seems it can catch people out so thought I'd mention it. Given our examples so far, we'd make the following change to our config/session.php file:
'domain' => '.my-awesome-app.io',
Locally, localhost alone is fine:
'domain' => 'localhost',
Assuming you've followed the rest of the instructions in the Sanctum documentation (setting axios.defaults.withCredentials = true;, adding the middleware etc) your backend configuration is now complete.
Front end and authentication.
I love Sanctum and I'm very grateful for the creators; so I say this with respect; the documentation lacks a little depth at this point. Grabbing the csrf-token is very straight forward, and then...
Once CSRF protection has been initialized, you should make a POST request to the typical Laravel /login route. This /login route may be provided by the laravel/jetstream authentication scaffolding package.
If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client.
It seems they've updated the docs!
As I write this, I've checked the latest docs and it's now highlighted the fact that you are free to write your own login endpoint. This was always the case, but might have escaped a few people, perhaps given the instructions above ("you should make a POST request to the typical Laravel /login route.") It's also perhaps not clear that you can override default Laravel methods to prevent unwanted side-effects of the default auth setup, like redirecting to the /home page etc.
Writing your own login controller is simple, and I prefer to do so for Sanctum. Here's one you can use:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
class LoginController extends Controller
{
public function login(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
'password' => 'required'
]);
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
return response()->json(Auth::user(), 200);
}
throw ValidationException::withMessages([
'email' => 'The provided credentails are incorect.'
]);
}
}
Feel free to modify this to suit your needs.
How you manage your state (making sure your app remembers you are logged in, for example) on the front-end is also up to you. There are lots of options, however if we're using Sanctum I think we should focus on a simple cookie-based approach. Here's one I use:
Login to your app. An auth session is established and your browser saves the cookies provided by your Laravel API.
Have your login script return the authenticated user (the one provided above does just that). Save the details of that user to your app state (eg. Vuex).
Check your state contains the user any time you need to secure an action against unauthorised users. Redirect to the login page is the auth check fails.
Here's the above in Nuxt.js form using middleware.
/middleware/auth-check.js
export default async function ({ store, redirect }) {
// Check if the user is not already in the store.
if (store.state.user === null) {
// Call your Laravel API to get the currently authenticated user.
// It doesn't matter if the store has been wiped out due to a page
// refresh- the browser still has the cookies, which will be sent
// along with this request.
try {
let rsp = await user.getAuthenticatedUser()
// If we get the user from the Laravel API, push it back in to
// the store and carry on to the page.
store.commit('SET_AUTH_USER', rsp.data)
} catch (e) {
// If our API doesn't return the user for any reason, redirect to
// the login page.
return redirect('/login')
}
}
// If not, carry on to the page.
}
/pages/admin.vue
export default {
middleware: auth-check
}
The code above is for example purposes, but it's generally what I use for Vue/Nuxt and Sanctum.
Hope this helps, happy to elaborate further if anyone can benefit.

Vue.js + Laravel SPA - Socialite stateless Facebook Login

First to say that I already implement session based facebook login with laravel socialite and I registered proper facebook acc for developers and create app, but because of SPA I need to change to stateless solution...
The redirect throws "Response for preflight is invalid (redirect)" and check the following config, images and code...
My vue.js app works on localhost:8080 and backend laravel app on localhost:8000
I installed barryvdh/laravel-cors an
d implement standard login with JWTToken
I already install laravel socialite
So, I setup config/services with proper credentials already
'facebook' => [
'client_id' => 'xxxxxxxx',
'client_secret' => 'xxxxxxxxx',
'redirect' => 'http://localhost:8000/login/facebook/callback',
]
I also register a good routes and I tested them 1000 times...
And when a GET send request it cannot be redirected on fb because of Response for preflight is invalid (redirect):
SocialiteController
public function redirectToFacebookProvider()
{
return Socialite::driver('facebook')->stateless()->redirect(); // this throws error !!!
}
the error which I get in console is like on picture
It should open to me a popup to login on facebook ?
Why I have trouble with this redirect ?
Any help ?

Laravel api routes with auth

I'm trying to make an api route that's only accessible if the user making the request is logged in. This is what I have in my routes/api.php but it returns
{"error":"Unauthenticated."}
Route::group(['middleware' => ['auth:api'], function () {
Route::post('schedules', ['uses' => 'Api\ScheduleController#store']);
});
Can this be done without laravel passport and how? I only need the route for in-app use for logged in users.
I assumed the login mentioned is on "web" which using "session" as driver.
Your are getting this issue because "web" and "api" guard is using different driver for authentication. Take a look in config/auth.php. The "api" guard is using "token" as it's default driver.
Thus, you have few options to encounter this.
Move the route for "schedules" in web.php. No worry, your ajax will failed if not authenticated. But, take note that anything that involved POST method will require csrf (_token parameter), unless you are using laravel axios
Using authentication using api also which you can refer this tutorial for "token" driver and all your secure routes will be using token in its Authentication header

Resources