Currently I have
Route::filter('pre_api', function($route, $request)
{
}
to do the api key validation and its working fine.
Is it possible to make a plain request to route api which allow client access to api without api key?
You can write api requests in routes/api.php.
You need to install the cors package in laravel.
toutrial for install core -> https://medium.com/#KrishaWeb/cross-origin-request-blocked-error-in-laravel-5-5-a733232795e4
example :
Route::group(['middleware' => 'cors'], function() {
Route::get('/hello',function(){
return 'hello';
});
});
Related
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'm working on a personal project using Laravel 5.6 and Axios library (standard Laravel 5.6 package).
I need to send first GET then POST request using Laravel's API and axios' http requests, but I'm not using Passport or any library like that since it's an internal API serving only VueJS to obtain stuff from the database.
If I set my API routes using auth:api middleware, I always get unauthorized response, whichever is the request type, this is the error output :
Error: Request failed with status code 401
The message :
message: "Unauthenticated."
But, as I read in the documentation, axios headers are set inside laravel's bootstrap.js to read the authorization token from meta tags (and the code is there) :
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// further down the file...
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
While, if needed, here's the http request code :
axios.post('/api/latest', formData)
So, why am I unauthenticated if those are set?
I've tried removing the auth:api middleware and, of course, it's working:
- Route::middleware('auth:api')->get('/latest', 'InternalApiController#latestEp');
+ Route::get('/latest', 'InternalApiController#latestEp');
What am I doing wrong?
I'm not using Passort or any library like that since it's an internal API serving only VueJS to obtain stuff from the database.
If the API is not stateless, meaning that the user is known to be logged in with a standard session cookie, then you can just use the default 'web' middleware for the API routes.
In the default RouteServiceProvider, change the mapApiRoutes function to use the web middleware instead:
protected function mapApiRoutes()
{
Route::prefix('api')
// ->middleware('api')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
That being said, you should really put the API routes behind the default 'auth' middleware since they're not throttled by default.
In the routes/api.php file:
Route::group(['middleware' => 'auth'], function() {
Route::get('/latest', 'InternalApiController#latest');
});
And if you want to ensure it's an AJAX request, you can create a simple middleware that checks that the request has the X-Requested-With header set to XMLHttpRequest.
class RequestIsAjax
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!$request->ajax()) {
return redirect()->route('login.index');
}
return $next($request);
}
}
And register it within the $routeMiddleware array inside the \App\Http\Kernel class.
protected $routeMiddleware = [
'ajax' => \App\Http\Middleware\RequestIsAjax::class,
With Laravel 8, I was getting a 401 error when trying to request something from the backend api. Following the accepted answer got me close, but it still didn't work. I believe this was because I have the dashboard on a subdomain as users.mydomain.tld and where I was using Axios was on the primary domain as mydomain.tld/some/path. The issue is the path for the session cookies. When re-configuring this, you need to clear all the cookies (dev toolbar). Then re-login like normal after you have made your changes. Else, the cookies will be messed up and you won't see it fixed or you wont be able to login at all until you clear them.
I reverted all the changes I made (following the accepted answer) and pinned down the SESSION_DOMAIN environment variable being the key ingredient, at least when it comes to the main user's area being on a sub-domain.
In your .env file:
SESSION_DOMAIN=".yourdomain.tld"
In app/Providers/RouteServiceProvider.php change api to web in the boot() method:
Route::prefix('api')
//->middleware('api')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
In routes/api.php:
Route::middleware('auth')->get('/user', function (Request $request) {
return $request->user();
});
In your resources/js/bootstrap.js:
window.axios.defaults.withCredentials = true;
After modifying the bootstrap.js file, you will need to run npm run dev again to recompile.
Now, you should be able to use Axios in your frontend JavaScript. Whether it be jQuery or Vue, whatever you are using.
axios.get('/api/user')
.then(response => console.dir(response.data))
.catch(error => console.log(error));
CSRF-token is not the same as an authorization token. Most likely you will manually need to add the authorization token, which is probably like this, depending on your method of authorization.
I'm currently building a laravel 5.4 powered page to manage users. I've done all basic pages such as home, login, register, dashboard using blade templating engine. Now I'm building the User Management page. I've successfully implemented VueJS for this particular page. All components are working perfectly.
Now the problem I'm facing now is using Axios to get logged in user data from API route. At first I'm using usual api route to get auth()->user() data but it doesn't work.
I've learned that I must use Laravel Passport to do this API operation.
These are the steps I made after that:
composer require laravel/passport
php artisan migrate
php artisan passport:install
Added the Laravel\Passport\HasApiTokens trait to your App\User model
Called the Passport::routes method within the boot method of your AuthServiceProvider
Set the driver option of the api authentication guard to passport
Added the CreateFreshApiToken middleware to your web middleware group
Edited bootstrap.js file like the following :
window.axios.defaults.headers.common = {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'X-Requested-With': 'XMLHttpRequest'
};
Axios Code :
axios.post('/api/getmydata', {
params: {
type: 'raw'
}
})
.then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
Changed route (api.php) :
Route::group(['middleware' => 'api'], function(){
Route::post('getmydata', 'ApiController#test');
});
Added function inside ApiController :
public function test() {
$user = Auth::user();
return $user;
}
The problem here is axios somehow return error: Unauthenticated
Is there anything wrong with my code?
Or is there any other way of achieving this? Thank you
Send the access token in the header of your API request:
Application Type :application/json
Authentication : Bearer [Access-Token]
I am running Laravel 5.4 and have my API routes setup with an API middleware that verifies an authentication token sent in the headers.
However, I want to avoid, or prevent the api/Login (route that generates the auth token) from being subject to the middleware.
Currently, in my API middleware, before any logic happens I have:
if(strpos($request->getUri(), 'Login')):
return $next($request);
endif;
I would like to remove checking if the route is the Login route before proceeding with the middleware logic. Is there a native way in Laravel to accomplish the above?
Note: all API routes are protected via an API middleware group which I have created in the Http/Kernel, then added the in the RouteServiceProvider.
You could add an except property in your middleware
Route::group(['middleware' => ['api'], 'except' => 'Login'], function () {
// Your Routes
});