Building an application with Laravel, Passport and Vue. This question does not directly pertain to logging in with oAuth2, but rather consuming the api that's protected by Passport with your own javascript code, as per the docs.
When accessing the home page and using axios to get /oauth, I get a error 401, as expected.
After logging in using the default login provided by laravel (uses web auth), I can go back to the home page, and the axios request for /oauth works great; for example /oauth/clients returns the clients of the logged in user, as expected.
mounted() {
//works as expected: 401 when logged out and response when logged in via /login
axios.get('/oauth/clients')
.then(response => {
console.log(response.data)
})
//Always returns 400 error
axios.get('/api/user')
.then(response => {
console.log(response.data)
})
}
However, when I try with axios to get /api/user, I get a 400 error, with the message Unauthenticated (regardless if before or after login, same error).
//Returns a 400, always
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
If I'm correct, the fact that the /oauth routes work proves that the laravel_token, csrf, and any such things are being sent correctly. Therefore, I think that this is a server side issue, especially with the auth:api guard. In the auth config file, I've set it to use Passport, and followed all the docs.
I'm confused as to why I get a 400 error and not a 401 when calling the api, and why it's not authenticating in the first place.
Especially frustrating since according to this video (11:30 mark), it's pretty much plug 'n' play.
Same behaviour with Postman.
Q: Any solutions to this error?
Full code on GitHub.
Add this code in your app/Http/Kernel.php
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
Related
Trying out this 'new' Laravel Breeze API package, trying to combine it with Sanctum.
Upon editing the code a bit I stumbled upon some weird error that I do not understand. When I try to authenticate with postman
public function store(LoginRequest $request)
{
if($request->authenticate())
{
$token = $request->user()->createToken('AccessToken')->plainTextToken;
return response(['AcessToken' => $token], 200);
}
else{
return response(['Status' => 'Unsuccesful login']);
}
}
This is the AuthenticatedSessionController.php I tried to edit it to return an Access token if the login is successfull, and to send a 'Status' response if the login has failed, instead what I got is, upon sending valid login credentials, I get the ['Status' => 'Unsuccesful login'], and if the Login credentials are not valid, I get redirected to the root page "/", any ideas?
From Laravel9 Sanctum is installed by default. This covers all api/ routes and is configured separately from Breeze. By default Sanctum will respond to cookie auth and accept/create tokens for token auth.
Routes in auth.php are used by Breeze. When you say postman I'm guessing you're hitting up an api route.
Here's the docs for Sanctum https://laravel.com/docs/9.x/sanctum
I'm trying to implement right now in my project a real time notification ui using laravel broadcast. I already made it work by broadcasting on a public channel but once I switched on to a private channel, the error POST http://localhost:8000/broadcasting/auth 404 (Not Found) appears when loading the page.
Here's what I made sure to check so far:
I've already uncommented both the App\Providers\BroadcastServiceProvider::class and Illuminate\Broadcasting\BroadcastServiceProvider::class, in the config\app.php,
I've also included Broadcast::routes(); and tested Broadcast::routes(['middleware' => 'auth:admin']); inside the boot() method of Providers\BroadcastServiceProvider if it'll work but still no dice,
I've also tried passing the Broadcast::routes(); in routes\web.php and,
Made sure that I have included <meta name="csrf-token" content="{{ csrf_token() }}"> in the main app.
The project that I'm working on implements SPA using Vue JS which is completely separated from the backend and is only connected through api. I hope someone could give me an insight with what going wrong with my methods. Thank you!
The question was asked a long time ago, but I hope it will be useful to someone. I had the same issue using stand-alone SPA (Vue.js) and REST API (Laravel). To simplify implementation of live comments system I used laravel-echo and pusher.js. To solve the issue I specified the auth endpoint according to the "Customizing The Authorization Endpoint chapter"
https://laravel.com/docs/8.x/broadcasting#customizing-the-authorization-endpoint
I used the following approach:
authEndpoint: process.env.VUE_APP_API_ROOT + '/broadcasting/auth'
where VUE_APP_API_ROOT is "http://api.yoursite.dev
But then I got a new issue with CORS. I used JWT authentication for API endpoints and a middleware from the https://github.com/fruitcake/laravel-cors package that allows to specify 'Access-Control-Allow-Origin' headers to solve the CORS issue when SPA sends requests to the API from a different domain.
So to solve the CORS and authentication issue I added the broadcast routes to api.php and set the JWT auth middleware
Broadcast::routes(['middleware' => ['auth.jwt:api']]);
and set a custom Pusher authorizer using Laravel echo
window.Echo = new Echo({
broadcaster: "pusher",
cluster: process.env.VUE_APP_PUSHER_APP_CLUSTER,
encrypted: true,
key: process.env.VUE_APP_PUSHER_APP_KEY,
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post(process.env.VUE_APP_API_ROOT + '/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
P.S. To provide axios with JWT token I used axios interceptors that allows to get token from Vuex.
axios.interceptors.request.use(config => {
const token = store.getters['auth/accessToken'];
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
});
I see quite a few people having a similar issue with this, but no final resolved solutions. I have been trying to get this working for about 24 hours now and still no luck!
Goals
Build and API using Laravel 6 and Dingo API
Be able to consume the API externally, authenticating with Passport oAuth.
Be able to consume the API internally, via ajax, using passports self-authenticating feature.
Be able to consume the API internally, with PHP, using dingo's self-consuming methods.
What I have found out so far
Auth provider order
Most solutions I have seen suggest setting up both the passport auth and dingo alongside one another. This is auth:api (passport) and api.auth (dingo).
// API route middleware
$api->group(['middleware' => 'auth:api', 'api.auth'], function (Router $api) {
...
The api.auth here is actually a custom auth provider setup in laravel and configured to dingo, which bridges the passport logic into dingo.
// Auth provider
class DingoPassportAuthProvider extends Authorization
{
protected $guard;
public function __construct(AuthManager $auth)
{
dump('DingoPassportAuthProvider Instantiated');
$this->guard = $auth->guard('api');
}
public function authenticate(Request $request, Route $route)
{
if ($this->guard->check()) {
return $this->guard->user();
}
throw new UnauthorizedHttpException('Not authenticated via Passport.');
}
public function getAuthorizationMethod()
{
return 'Bearer';
}
}
// Configured in dingo (Api.php)
'auth' => [
'passport' => \App\Providers\DingoPassportAuthProvider::class,
],
If we put the dingo API provider first in the middleware stack we get:
Internal API requests work IF you specify the user for the call with the be() method: $this->api->be($request->user())->get('/api/profile')
External API requests and internal AJAX requests authenticate correctly and the user is returned from the custom dingo auth provider, however, for some reason you cannot then access this user from within the API controllers: $user = $request->user(); // null
If we put the Passport API provider first in the middleware stack we get:
Internal API requests do not work at all (401 always returned)
External API requests and internal AJAX requests work as intended.
The authenticate method on the dingo passport provider is no longer called. I think this may have something to do with the 401 returned on internal calls.
I believe the correct way around, is to put the passport authentication first. This way, we authenticate the user before calling the dingo authentication, resulting in 2 things:
Passport works natively as expected.
Dingo internal API calls should now just be able to be called with $this->api->get('/api/profile') (omit defining the user with be()), however this does not work.
At the moment I have the previous configuration. Passport works as intended for external and ajax calls, but the internal dingo calls always return 401.
There are a few boilerplate templates I have checked out and they do not seem to do anything different. I wonder if something changed in L6 to explain why the internal requests do not work.
I have found one work around for now, which gets most of the way there...
Within the custom dingo auth provider:
class DingoPassportAuthProvider extends Authorization
{
public function authenticate(Request $request, Route $route)
{
if (Auth::guard('web')->check()) {
return Auth::guard('web')->user();
}
if (Auth::guard('api')->check()) {
$user = Auth::guard('api')->user();
Passport::actingAs($user);
return $user;
}
throw new UnauthorizedHttpException('Not authenticated via Passport.');
}
public function getAuthorizationMethod()
{
return 'Bearer';
}
}
This now checks to see if the request is coming from either the web guard (internal request) or the api guard (external or ajax request) and returns the correct user.
For the api guard, there seems to be an issue that the user is authenticated but not actually available within the controllers. To get around this I added the Passport::actingAs($user). It is probably not best practice, but the guards are now acting as they should and as are all my different scenarios.
Then in the API route middleware, we only specify the custom dingo provider.
// API route middleware
$api->group(['middleware' => 'api.auth'], function (Router $api) {
...
One thing to note with this, is dingos be() method does not work quite as expected. Instead you need to switch the user as you would in a general laravel app.
\Auth::loginUsingId(2);
$user = $this->api->get('/api/profile');
I created an SPA with Laravel 5.6, Vue 2.5 and Laravel Passport which is working quite well. I really love Laravel and Vue as they make building SPAs with APIs very easy and a lot of fun.
After setting up Laravel Passport as described in the docs the login as well as calls to the API are working as expected based on the 'laravel_token' which is correctly returned and stored in the cookie.
However, my problem is that my users are using the app for a pretty long time, without reloading the page but only performing calls against the API with axios. Somehow Laravel does not refresh the 'laravel_token' (and the corresponding cookie) in API calls (it does so, when I call a 'web' route). Consequently, the 'laravel_token' expires t some point and the user needs to log in again.
How can I force Laravel to refresh the 'laravel_token' (and thereby prolong its validity) with every call of an API route from axios?
Any help is very much appreciated!
I solved a similar issues in the past by creating a simple route (in the web middleware group) to keep the session alive for as long as the browser tab is open.
In routes/web.php:
Route::get('/keep-alive', function () {
return response()->json(['ok' => true]);
})->middleware('auth');
And then ping this route periodically with javascript:
setInterval(() => {
axios.get('/keep-alive')
.then(() => {})
.catch(() => {})
}, 600000)
I go into a little more detail here: https://stackoverflow.com/a/57290268/6038111
Axios has a way to "intercept" / see if a call failed. Inside the error callback I am seeing if it was an unauthenticated error and simply reloading the page.
Admittedly, I would love to be able to write another Axios call inside the error caught block to grab another session token "laravel_token", but have yet to find a way to do it. Reloading the page will refresh the laravel_token though, so it solves my problem for now. ¯\_(ツ)_/¯
After-thought: I'm actually thinking you probably couldn't refresh the laravel_token through an Axios call because you'e already dropped the session. I'm guessing you have to do it this way.
// Refresh Laravel Session for Axios
window.axios.interceptors.response.use(
function(response) {
// Call was successful, don't do anything special.
return response;
},
function(error) {
if (error.response.status === 401) {
// Reload the page to refresh the laravel_token cookie.
location.reload();
}
// If the error is not related to being Unauthorized, reject the promise.
return Promise.reject(error);
}
);
I am building a REST user-microservice using Laravel 5.5 + Passport.
I am using the standard Passport::routes(), but I have had to modify the Auth::routes in order to make them return JSON responses, and to make them work with Passport.
I have added the following lines to my routes/web.php file:
Route::group(['middleware' => 'auth:api'], function () {
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
});
This allows me to POST https://myapi/logout
If I make the call with the header "Authorization => Bearer TOKEN", I get a successful logout response.
If I provide no header at all, I get a "not authenticated" message (which is good)
However, if I provide the header with a revoked token, I get a recursive deadloop of the function: Illuminate\Auth\RequestGuard->user() (it keeps calling itself recursively until stack-overflow)
This is all done in the auth:api middleware, my logout code is not reached, but my LoginController constructor is called. Constructor code:
public function __construct(Application $app)
{
$this->apiConsumer = $app->make('apiconsumer');
$this->middleware('guest')
->except('logout');
}
I'm struggling to understand if it's my code causing this issue, or some combination of Laravel + passport + auth.
My first thought was that the auth:api middleware fails to authenticate the user, and as a result redirects the user to /home, where for some reason it's triggered again, recursively. But if that was the case, why would it work correctly with no header?
My current thinking is that the token in question does exist in the database, but Laravel is failing to figure out that it's revoked.
Any suggestions appreciated,
I found an answer (if not the answer) after a lot of research. It appears this is a Laravel bug (https://github.com/laravel/passport/issues/440). The solution is to add OAuthServerException to the $dontReport array in app/Exceptions/Handler.php:
class Handler extends ExceptionHandler
{
protected $dontReport = [
...
\League\OAuth2\Server\Exception\OAuthServerException::class,
];
}
This will avoid trying to log user information, thereby avoid the deadloop.
I have faced this in localhost. in my case, I have used xampp server and facing this issue
after creating a virtual host like "testlarave.test" then solve the error