Passport Auth Error when Accessing API Routes from Web - laravel

I'm using axios to request some data that requires that the user is logged in. I've tried a couple things to get the 401 to go but have had no joy yet. The API is on a subdomain and I think this might be why I'm having issues. I also have the session set to .domain.tld.
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class is in on the web middlewares. When I use the passport demo the authorization works just fine. However, when I send an axios request to an API route on the subdomain, it doesn't have any of the Set-Cookie headers that the demo sends to the web routes. I then tried putting in window.axios.defaults.headers.common['Authorization'] = 'Bearer (laravel_token here) with no luck as well.
How do I get my API endpoints (on the api subdomain) to work with Laravel's in-app Passport authentication?

It might be an issue with axios itself. Use Guzzle instead to opt out the possibility that its axios.
$http = new \GuzzleHttp\Client;
$response = $http->request('POST', 'your api url here', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$access_token,
],
'form_params'=>[
//if you have any data to send as a post request then put it here.
]
]);
Set your Accept parameter as well. Give it a roll.

Figured it out! Needed to enable withCredentials in Axios.
axios.defaults.withCredentials = true;

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"

Using response key from api as middleware in Laravel

I'm creating a web app & keeping my web client and backend API completely separate. I want to use the token from sign in api response for every url in my web app route as an middleware authentication. So if I don't have response from sign in api I cannot access the url.
I have sign in api like this, using username & password as header. I'm using this api when login to my web app:
$client = new Client();
$credentials = base64_encode('username:password');
$response = $client->get('sign-in-url',
[
'headers' => [
'Authorization' => 'Basic ' . $credentials,
],
]);
...
return $response;
My question is how to protect every url on my web app using that token? If it is in web app I usually using middleware to check if user already logged in or not. But what I need to do if using api?
You can define a middleware and put it's address inside $middleware array of your App\Http\Kernel class. It will be applied on every request (both web and api).

Using Sanctum with regular Laravel authentication

I have a simple web app using regular authentication for all of my web routes. There are just a few places in my app where I want to be able to call a few API routes from Javascript. Can I setup Sanctum's SPA authentication to work without doing an SPA-style login?
I have followed the instructions server-side, and on my login page I am doing a CSRF cookie request using the axios library before the user logs-in using the standard routes. But when I try to then hit a Sanctum protected route I just get redirected to the home page.
Is it expected that Sanctum session-based auth should work with a regular app login?
Edit
I located the problem. In EnsureFrontendRequestsAreStateful is this function:
public static function fromFrontend($request)
{
$referer = Str::replaceFirst('https://', '', $request->headers->get('referer'));
$referer = Str::replaceFirst('http://', '', $referer);
return Str::startsWith($referer, config('sanctum.stateful', [])) ||
Str::is(config('sanctum.stateful', []), $referer);
}
$referer is null on my requests, so this function cannot return true. If 'referer' is changed to 'host' it works. Is this acceptable? Does it satisfy the point of the function still, that the "given request is from the first-party application frontend" or are there security implications I haven't considered?
'Referer' works when hitting the API endpoint from Javascript, but when hitting it from Postman or in a browser window the headers non-existence causes a problem. So in simple GET testing it is redirecting to login, but is working fine when called from the Axios library in a Vue component.
'Referer' works in postman too, you need only add header to your request Referer -> {{host}}. host it's postman variable or you can type it like localhost. Sanctum by default checking this referers localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1, but you can specify your own referer by adding variable to env file SANCTUM_STATEFUL_DOMAINS=your-app-domain.com here.

Always response 401 (Unauthorized) for my Laravel Passport generated OAuth 2 token

I am using:
Postman/Insomnia for REST checking
Laravel 5.6 with Laravel Passport
Vagrant (Apache 2, PHP 7.2)
Made all checklist described on Laravel Docs for Laravel Passport and after certain steps I receive HTTP 401 for my valid OAuth access token.
Requested by /oauth/token/ the new access token with client_id and client_secret.
Used received access token to authorize my simple Laravel REST test controller with included Oauth api middleware.
The end is one: 401 unauthorized :(
So, here is some of my configurations:
Apache
api route
Kernel.php
PassportServiceProvider.php
AuthServiceProvider.php
I had a very similar issue too:
The difference between your codes and mine:
In the routes/api.php, i used only auth:api.
I didn't create PassportServiceProvider.php in the app folder.
In the Kernel.php mine is client not client_credentials.
I used client_credentials as grant_type in the POST request call.
In the result I always got 401.
Until I created a user using Password Grant Client:
php artisan passport:client --password
And changed client_credentials to password in the POST request call:
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor#laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
$access_token = json_decode((string) $response->getBody(), true)['access_token'];
Put the access token returned in the Bearer of the headers, and it works.
And also you can get the current user using $request->user();
If you are using client_credentials as grant_type, it's going through the client middleware, so in the middleware auth:api needs to be removed.
This is because Apache does not, by default, pass authorization headers to PHP. You need to edit your Apache site configuration to add a line to Deskpro's directive. Note that this configuration must be added directly to Apache's configuration (e.g., adding it to htaccess will not work
<VirtualHost>
# ...
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
# ...

Laravel 5.3 and VueJS 2 Authenticating

Im using Laravels default auth to lock down some paths in the routes/api.php file.
Route::get('/projects', 'ProjectController#index')->middleware('auth:api');
I log the user in via a PHP form (not via a http request via Vue).
This creates the session and when I submit a HTTP request via Vue I can see that the header includes the Cookie and X-CSRF-Token however I keep getting a 401 {"error":"Unauthenticated."}
In my config/auth I have api driver set as 'token' (have tried changing this to 'session' but that did work :/)
From my understanding and what I have read online I should be able to use the default Laravel auth functionality to accomplish API calls from Vue HTTP requests.
this.$http.get('/api/projects')
.then(response => {
this.projects = response.body;
})
.catch (err => {
console.log(err);
});
I've read about methods of authenticating by generating an JWT token and storing that in local storage when the user logs in. Is this the method I should use or should I be able to accomplish it with the default Laravel Auth middleware?
Hope my questions make sense, any help/advice would be appreciated.
The auth:api middleware doesn't use cookies, it uses api_token param, which can be passed via get or Bearer <token> header. Just use web middleware.
I suppose you need to access the same route in two ways - for API users and for browser users. So why don't you create two routes for one action?
// api group with /api prefix
Route::get('/projects', 'ProjectController#index')->middleware('auth:api');
// web group
Route::get('/projects', 'ProjectController#index')->middleware('web');

Resources