Can't get auth user with laravel passport, keep getting "Unauthenticated" error - laravel

I can't get the information of the authenticated user in a Laravel passport app with JWT and vue.
I've installed laravel passport. Ive done everything in the documentation and added:
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
To consume it with js for a SPA app.
I've protected my routes with the auth:api middleware, but i keep getting:
{"Status":{"api_status":0,"Code":401,"Message":"Unauthenticated"}}
When i use postman to manually insert the CSRF-TOKEN in Authorization Bearer Token. It does give me the auth user.
Whatever i do, i keep getting null on Auth::user(); in my controllers and routes
Laravel V5.7
Node V10.15.3
Npm V.6.9.0

You need to send a POST request (using Postman/Insomnia) with the details of the user you want to log in as to /oauth/token in your app which will respond with an API token. You save this api token locally, and then add add it as a Header variable to your guzzle/axios/whatever function's http calls (every one of them!) to your API.
$http = new GuzzleHttp\Client;
$data = 'Whatever';
$response = $http->request('POST','api/user',[
'data' => $data,
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer xxxxxxxxxxxxThis is the long authentication string returned from the Postman requestxxxxxxxxxxxxxx'
]
]);
dd(json_decode((string) $response->getBody())); // To view the response
From: https://laravel.com/docs/5.5/passport#creating-a-password-grant-client

Related

Make a post request to a api that uses sanctum via Client (Laravel-6.2)

I have 5 applications that use the same sanctum API for authentication. What I really want to do is to make a POST request sanctum API from another application. I do GET requests like the below and it's working. But when I make a POST request it returns csrf token mismatch error.
So could someone please tell me is it possible to make a post request into a sanctum API via Client?
$response = $client->get('http://localhost:8000/api/user', [
'headers' => [
'accept' => 'application/json',
'cookie' => $request->header('cookie'),
'referer' => $request->header('referer'),
]
]);
Thanks
i use Bearer instead of cookie ,
in sanctum laravel reads access tokens from Authorization in header.
this is how i make request from laravel client to sanctum (an example of my code):
$response = Http::withHeaders([
"Accept"=> "application/json",
"Authorization" => "Bearer " . Cookie::get("Laravel")
])
->post("http://localhost:8088/api/v1/url/find", ["find" => $request->find]);
i send my headers with method withHeaders as an associative array
and send my post body with post method second argument.
read more here : https://laravel.com/docs/9.x/http-client
You're missing some headers on your requests. You need to first make a request to "/sanctum/csrf-cookie", what gives you a CSRF token. Then you pass that token as the value for the header "X-CSRF-TOKEN". Sanctum documentation explains it very well. https://laravel.com/docs/8.x/sanctum#spa-authenticating
Wish you luck!

How to verify a token with Laravel Passport?

I want to validate a token using Laravel Passport. My API's consumer will pass the token via the Authorization header as a Bearer token and I want that Laravel Passport returns me if is a valid token.
I don't want to use a middleware, my API will be in another Laravel Project, but I want this project to call the Laravel Passport server just for check if a token is valid, how can I check the token?
I'm issuing the tokens right, just left verify them, but I don't know how:(
This is how you can verify tokens without the middleware:
Auth::guard('api')->check();
You can create your own middleware. Inside that middleware's handle, pick the Bearer token and call your Passport server, depending on the response returned call next if true, or abort if false. Something like this:
public function handle($request, Closure $next)
{
try {
$passportEndpoint = 'your_passport_endpoint_here';
$client = Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => $request->header('Authorization')
]);
$response = $client->get($passportEndpoint);
if ($response->status() === 200) {
$body = $response->object();
//do some stuff with response here, like setting the global logged in user
return $next($request);
}
}
catch (RequestException $exception) {
}
return abort(401, 'You are not authenticated to this service');
}
If you don't want to use the Passport middleware in the project where you want to validate the tokens, you would have to create an endpoint in the Laravel Passport server that can accept the token, perform the usual Passport validation and return a response to your service.
It would be an implementation of the Token Introspection spec: https://www.rfc-editor.org/rfc/rfc7662 - though you have to implement it yourself, as I think that Laravel Passport doesn't support it out-of-the-box.
Also, when verifying JSON Web Tokens (if this is the type of tokens that you use), remember that verifying the signature is not enough. Have a look at this best practices article to know how to properly work with JWTs.

Laravel Passport: CreateFreshApiToken in App: Authorization Failed

I'm attempting to install Laravel Passport on Laravel 5.7.18 using PHP 7.2.13.
My application consumes the API within itself using JavaScript (Axios with Vue)
I'm getting a 401 Unauthorized error within the JavaScript web application. I've read the documentation and added CreateFreshApiToken to the web Kernel. The laravel_token cookie is in fact setting itself. However, the oauth tables are clean in the database.
Http/Kernel:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
JavaScript:
axios.get("api/users/" + id).then(({ data }) => {
this.user = data;
});
Auth.php:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Routes (Api.php):
Route::middleware('auth:api')->group(function () {
Route::resource('users', 'UserController');
Route::resource('groups', 'GroupController');
// .. plus more resources
});
Axios Config:
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
var 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');
}
Headers in Browser that returns 401:
Working Request with Postman:
If you are using a username and password for initial login then the assumption is you are building a First Party application that has the right to make a user/pass login attempt.
It is highly recommended that if you are building a Reactive app using
Angular, React.js or Vue.js then an SPA (Single Page Application)
approach will yield a much more robust product.
https://en.wikipedia.org/wiki/Single-page_application
You should note that with this particular method, if your application
makes a static (none ajax request) and thus reloads in the browser,
you will loose the auth token. In this case you are not playing host
to an app that is by it's very definition an SPA, so if you need to retain the token between
static request reloads then you need to store the token in a cookie, I
suggest using a cookie rather than localStorage because the availability of
localStorage is not 100% guaranteed to be at your disposal in all web browsers.
If your application is on the same domain, you do not need to use
Passport. Instead native session cookie auth is perfectly fine, all
you have to do is make sure you are passing the CSRF Token for post
requests.
For user/pass token grants you should follow this guideline:
https://laravel.com/docs/5.7/passport#password-grant-tokens
From that guide, when you make a successful request to /oauth/token, the returned token should be set in your application as an Authorization header with Bearer token.
The token request response looks like this:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKVJhb...nheKL-fuTlM",
"refresh_token": "def502008d6313e...94508f1cb"
}
You should request and handle that JSON object as follows:
axios.post('/oauth/token', {
grant_type: "password",
client_id: "1",
client_secret: "zkI40Y.......KvPNH8",
username:"email#address.com",
password:"my-password"
}).then( response => {
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.access_token}`
} );
The values for client_id (id) and client_secret come from oauth_clients table, there should already be an entry in there.
If not then run php artisan passport:client --password
Don't forget that you will have to configure some headers, look to this post has some relevant information for the Oauth Authorization header:
How to send authorization header with axios
If you say your tables are clean in the database, try to run this command again:
php artisan passport:install
This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens:
Laravel 5.7 Passport docs

How to authenticate Vue.js / Axios request of an API route in Laravel

I'm in Laravel 5.6. I have all my API routes built out and properly responding to requests from my REST client (Paw). I'm trying to build a simple front end to access those routes.
I'm trying to use Laravel's out-of-the-box features as much as possible, so I'm using Axios to call those routes from a blade template using Vue.js. It works if I disable auth middleware on the test route, but I get 401 errors on the console when auth middleware is enabled for the route.
The problem seems obvious enough... The auth:api guard on my /api routes wants to see an oauth token in the header, but when I log in with the web page it does session authentication. I assume there's a simple way to resolve this without having to spoof an oauth token request in the web frontend, right? Do I need to somehow pass the session token in my request with Axios? And, if so, do I also need to change the auth:api guard in my api routes file?
I solved it! I'm a bit embarrassed because the answer was actually in the Laravel docs, but I will say I tried this before posting the question here and it wasn't working. Perhaps something else was broken at the time.
Per the Laravel docs:
All you need to do is add the CreateFreshApiToken middleware to your
web middleware group in your app/Http/Kernel.php file:
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
This Passport middleware will attach a laravel_token cookie to your
outgoing responses. This cookie contains an encrypted JWT that
Passport will use to authenticate API requests from your JavaScript
application. Now, you may make requests to your application's API
without explicitly passing an access token...
You will probably want to use Larvel Passport or a JWT auth mechanism for obtain the Authorization token.
Seeing as how you're using axios, add a request interceptor to attach the access token to every request once you successfully authenticate. A simple example:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// assume your access token is stored in local storage
// (it should really be somewhere more secure but I digress for simplicity)
let token = localStorage.getItem('access_token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
to use the auth:api first you need api_token inside your users table
Schema::table('users', function ($table) {
$table->string('api_token', 80)->after('password')
->unique()
->nullable()
->default(null);
});
also you can create a user for testing as follows
User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'api_token' => Str::random(60),
]);
in your layout use the following before #yield('content')
<script>
window.Laravel = <?php echo json_encode(['api_token' => (Auth::user())->api_token]); ?>
</script>
now you can use window.laravel.api_token inside your vue js to use it in headers
heres an example
var methods = new Vue({
el: '#tabs_lists',
data: {
config: {
headers: {
Authorization: 'Bearer ' + window.Laravel.api_token,
Accept: 'application/json'
}
},
data: []
},
methods: {
test: function (link) {
axios.get(link, this.config)
.then(response => (this.data = response.data)).catch(function (error) {
// handle error
console.log(error);
});
}
}
}
)

How to construct a private sever-to-client-side API? Laravel Passport?

I'm not sure if I'm using the correct method for my problem.
I would like to get Infos via Axios. For example: calling: /api/user with Axios gets me the user, but I don't want to see the information when I go on domain.test/api/user.
I even want to use API calls to get Results of Functions even if the User is a Guest.
I installed everything there is on the Laravel Documentation for API, but still I'm not sure how the user gets the Token.
So if I call:
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
I get from the network tab {"message":"Unauthenticated."}. (I didn't forget to set: ...Middleware\CreateFreshApiToken::class and everything there is).
I think my problem is that I didn't register the User correctly. I got my two keys what should I do with it?
And then it's weird, reading the blog https://laravelcode.com/post/laravel-passport-create-rest-api-with-authentication, they use
$success['token'] = $user->createToken('MyApp')->accessToken;
But I don't get it. Where do I save it? I'm super confused, because every blog about shows Laravel Passport completely differently.
Or am I doing it wrong?
Passport is an OAuth2 server implementation which offers several different authorization strategies. While it is certainly possible to use Passport for authentication, it's primary purpose is authorization. This is done via token scopes. The type of token you choose to issue depends on your intended purpose with the API and whom your intended target is to be consuming your api.
Let's start with the following:
$success['token'] = $user->createToken('MyApp')->accessToken;
Here they are creating Personal Access Tokens. This is the simplest means of consuming your api, since clients are able to create the token themselves as needed and are long lived.
The Password Grant strategy is also a good option. A common approach when using this strategy is proxying authentications to Passport internally and merging the client id and secret into the request in a middleware.
public function handle($request, $next) {
return $next(tap($request, function($request) {
$request->merge([
'client_id' => config('services.passport.client.id'),
'client_secret' => config('services.passport.client.secret')
]);
// do any other pre-request work needed here
}));
}
Setup
To get started with Passport, run the command:
php artisan passport:install
This will create the encryption keys as well as a personal access and password grant client types, which will be stored in your database in the oauth_clients table.
Guard and Middleware
API routes need to be using the api guard, done by setting auth:api middleware on the route group(s) or Controller constructor.
// using via constructor
public function __construct()
{
$this->middleware('auth:api');
}
// via a route group
Route::group(['middleware' => ['auth:api'], function() {
// your api routes
});
Passport Routes
Your user model needs to be using the trait HasApiTokens and within the AuthServiceProvider call Passport::routes() in the boot method.
public function boot()
{
$this->registerPolicies();
Passport::routes();
// this is also an ideal time to set some token expiration values
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
}
To use the personal access token strategy for authentication, you need to create your own login and logout routes instead of using the builtin Laravel auth mechanisms.
Referring to the tutorial in your question, they have defined the following routes:
Route::post('login', 'API\PassportController#login');
Route::post('register', 'API\PassportController#register');
and the login method implemented as:
public function login(){
if(Auth::attempt(['email' => request('email'), 'password' => request('password')])){
$user = Auth::user();
$success['token'] = $user->createToken('MyApp')->accessToken;
return response()->json(['success' => $success], $this->successStatus);
}
else{
return response()->json(['error'=>'Unauthorised'], 401);
}
}
This is expecting a ajax request with an email and password to be posted to login and if successful it responds with an access token.
Storing/Using the Token
This token must be stored client side, either in a cookie or local storage, for example, and then added to every request from the client there after. You'll add the token to the Authorization header in client requests.
// Use a response interceptor to check for a token and put it in storage
axios.interceptors.response.use(response => {
// store the token client side (cookie, localStorage, etc)
if (response.data.token) {
localStorage.setItem('token', token)
}
return response
}, error => {
// Do something with response error
return Promise.reject(error);
});
// Use a request interceptor to add the Authorization header
axios.interceptors.request.use(config => {
// Get the token from storage (cookie, localStorage, etc.)
token = localStorage.getItem('token')
config.headers.common['Authorization'] = `Bearer ${token}`
return config;
}, error => {
// Do something with request error
return Promise.reject(error);
});
Accessing the Authenticated User
In order to get the user, pass the 'api' parameter to the auth() helper, Auth facade, or through the request:
$user = auth('api')->user();
$user = Auth::user('api');
$user = request()->user('api');
Keep in mind personal access tokens are long lived, so it's up to you to decide when and how they should expire.
Any http client can be used to make api requests, axios is a very common option and included with each Laravel installation.

Resources