How to create a JWT Token without authentication - php

"laravel/framework": "5.7.*"
"tymon/jwt-auth": "dev-develop"
I am trying to create a JWT token with added custom claims, without auth (which means that I am not looking to create a token from credentials.) This is for the purpose of creating tokens that do not require logins, such as forgot/reset-password, etc.
using Tymon/JWTAuth (https://github.com/tymondesigns/jwt-auth)
Since there was an issue with Latest Laravel, it was recommended to load the latest dev ( 1.0.x-dev ).
I have tried the following code without avail:
class OTL extends Model implements JWTSubject
use JWTAuth;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Tymon\JWTAuth\Facades\JWTFactory;
public static function getJwtToken($customerId, $action, $token){
$customClaims = ['action' => $action, 'customer-id' => $customerId, 'token' => $token];
$factory = JWTFactory::customClaims($customClaims);
$payload = $factory->make();
$token = JWTAuth::encode($payload);
return $token;
I am receiving an error: JWT payload does not contain the required claims.
I am expecting to receive the token that holds the payload above.

I solved this problem by commenting out 'exp' in required_claims in the config/jwt.php:
.....
'required_claims' => [
'iss',
'iat',
//'exp',
.....
],

I just encountered the same error and was able to solve it by updating the list of required claims in the generated configuration file config/jwt.php.
...
'required_claims' => [
'exp',
'sub',
...
],
...
Also make sure you have run php artisan jwt:secret or otherwise provided a secret key for signing.

Related

Unauthorized 401 error in laravel 6 passport

I'm trying to use the laravel passport to authenticate users. I followed every step in laravel passport documentation. But it shows error when I access a route that is protected by 'auth:api' middleware. I've searched over the internet but any of the answers are not working so far. Please see the code, and what I've tried so far below.
Tech used
Laravel 6
Laravel Passport
Vue 2
Axios
Tried so far:
Changed Passport::routes(); to Passport::routes(null, ['prefix' => 'api/oauth', 'middleware' => ['auth:api', 'web', 'auth']]); in AuthServiceProvider. Also tried Passport::routes(null, ['prefix' => 'api/oauth');
Tried to run php artisan cache:clear
routes/api.php
Route::middleware('auth:api')->group(function() {
Route::get('user', function() {
return request()->user();
});
Route::get('posts', 'PostController#index')->name('posts');
Route::post('posts/store', 'PostController#store')->name('posts.store');
Route::get('posts/{id}/show')->name('posts.show');
Route::get('users/{id}/show')->name('users.show');
});
Axios
mounted() {
axios.get('/api/posts')
.then(res => this.posts = res.data).catch(error => console.log('Unable to fetch posts.'))
}
Error
Headers:
Please let me know if you need some details that is not indicated on my post yet.
Setup
Run composer require laravel/passport:9 for the installation.
Run php artisan migrate for the tables.
Run php artisan passport:install to generate the encryption keys.
Added Laravel\Passport\HasApiTokens trait in User model.
Added Passport::routes(); in AuthServiceProvider.
Changed guard's api driver to passport in config/auth.php.
Full source code:
Click this link
I think you should review your code with the following steps.
Step 1: User model has to use HasApiTokens
...
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
Step 2: Passport::routes method within the boot method of your AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
...
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
Step 3: In your config/auth.php configuration file, you should set the driver option of the api authentication guard to passport
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Step 4 (Optional):
If you want to consume your API from your JavaScript application, you need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. 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,
],
! You should ensure that the CreateFreshApiToken middleware is the last middleware listed in your middleware stack.
Edit (2021/10/17):
You should try to add the auth middleware to $middlewareGroups of your Kernel.php
protected $middlewareGroups = [
...
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
'auth:api', //Need to add
],
];
Edit (2021/10/25):
I downloaded your code and debug it. When you debug it, you will get the following result:
Header JWT is not valid for JSON structure. There seems to be an incompatibility between the Laravel Passport library and your system. You should check your system version (PHP, Laravel, etc). I will continue to investigate. If you have any new information, please let me know
Edit (2021/11/02):
Update your laravel/framework dependency to ^6.0 in your composer.json file. If installed, update your laravel/passport dependency to ^9.3.2 in your composer.json file.
https://laravel.com/docs/6.x/upgrade#updating-dependencies
According to Laravel 6.x documentation, the version of Laravel Passport is from 9.3.2. Please try this version.
If you are using Api/oauth prefix AND add the auth:api middleware than you need to authenticate via Bearer tokens.
So it may be you have csrf token related issue so try below to disable it.
Passport::ignoreCsrfToken(true);
You should add token bearer into your ajax requests. You can get it after successful login.
// laravel
use Illuminate\Support\Facades\Auth;
use App\Models\User;
public function login()
{
$credentials = request(['email', 'password']);
$user = User::where('email', $credentials['email'])->first();
if (!$user || !$token = Auth::attempt($credentials)) {
return response()->json(['success'=> false, 'error' => 'неверный логин или пароль', 'cr' => $credentials], 422);
}
return $this->respondWithToken($token);
}
// vue login
async function login(email, password) {
try {
const response = await axios.post('/auth/login', {email, password});
const token = response.data.token;
// some func to store token, I am using vuex mutations for this
storeToken(token);
}
catch(error) {
console.error(error);
}
};
// vue regular function
async function changePassword(newPassword) {
// get token from global storage, with vuex it will be like store.state.auth.token
const token = getToken();
await axios.post('/user/changePassword', {newPassword}, headers: { Authorization: `Bearer ${token}`});
}
You've forgotten to run php artisan passport:keys ...so there may be no RSA key-pair available, which is crucial. Clients usually can be added with: php artisan passport:client. And generally speaking, there is no default recipe for debugging Laravel middleware ...there only is xdebug.

RuntimeException - Session store not set on request - Laravel Socialite - Facebook

I'm using Laravel 5.7 and Laravel/Socialite 3.1.
I want to login using a Facebook app I just configured for this project.
These are the main files I have configured for this:
/.env
...
FACEBOOK_CLIENT_ID=***
FACEBOOK_CLIENT_SECRET=***
FACEBOOK_CALLBACK_URL=http://localhost:8000/auth/facebook/callback
/config/services.php
<?php
return [
...
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_CALLBACK_URL'),
],
];
/routes/api.php
<?php
use Illuminate\Http\Request;
...
Route::get('auth/facebook', 'SocialiteController#redirectToProviderFacebook');
Route::get('auth/facebook/callback', 'SocialiteController#handleProviderCallbackFacebook');
/app/Http/Controllers/SocialiteController.php
<?php
namespace App\Http\Controllers;
use Socialite;
class SocialiteController extends Controller
{
public function redirectToProviderFacebook()
{
return Socialite::driver('facebook')->redirect();
}
public function handleProviderCallbackFacebook()
{
$user = Socialite::driver('facebook')->user();
print_r($user->token);
}
}
My problem is that for some reason I get the error:
RuntimeException
Session store not set on request.
as you can see on the following image:
I don't want to use sessions on this project at all. I just want to get the token from Facebook on the callback inside: function handleProviderCallbackFacebook().
Any idea on how to solve this issue?
Thanks!
From the Socialite documentation
The stateless method may be used to disable session state verification. This is useful when adding social authentication to an API:
return Socialite::driver('google')->stateless()->user();

How to use Laravel Passport with Password Grant Tokens?

I just read the https://laravel.com/docs/5.6/passport documentation and I have some doubts that hopefully someone could help me with:
First, some context, I want to use Passport as a way to provide Oauth authentication for my mobile app (first-party app).
When I use php artisan passport:client --password I get back a Client ID and a Client Secret. Does this value have to be fixed on my app? for example storing them hardcoded or as a "settings" file? If the values shouldn't be stored then how should it work?
To register a user to my app I use: $user->createToken('The-App')->accessToken; I get that the accessToken will be the one used for sending on all my requests as a Header (Authorization => Bearer $accessToken) but what exactly is "The-App" value for?
For login the user I'm using the URL: http://example.com/oauth/token and sending as parameters:
{
"username": "user#email.com",
"password": "userpassword",
"grant_type": "password",
"client_id": 1, // The Client ID that I got from the command (question 1)
"client_secret": "Shhh" // The Client Secret that I got from the command (question 1)
}
When I login the user using the previous endpoint I get back a refresh_token, I read that I could refresh the token through http://example.com/oauth/token/refresh but I try to request the refresh I got Error 419, I removed the url oauth/token/refresh from the csrf verification and now I get back "message": "Unauthenticated.", I'm making the following request:
Content-Type: x-www-form-urlencoded
grant_type: refresh_token
refresh_token: the-refresh-token // The Refresh Token that I got from the command (question 3)
client_id: 1 // The Client ID that I got from the command (question 1)
client_secret: Shhh // The Client Secret that I got from the command (question 1)
scope: ''
Should I use this endpoint? or is not necessary given the app I'm trying to develop.
Finally, there are a lot of endpoints that I get from passport that I don't think I will use for example: oauth/clients*, oauth/personal-access-tokens* is there a way to remove them from the endpoints published by passport?
Thanks a lot for your help!
If you are consuming your own api then you don't need to call http://example.com/oauth/token
for user login because then you need to store client_id and client_secret at app side. Better you create an api for login and there you can check the credentials and generate the personal token.
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
// Authentication passed...
$user = Auth::user();
$token = $user->createToken('Token Name')->accessToken;
return response()->json($token);
}
}
Finally, there are a lot of endpoints that I get from passport that I
don't think I will use for example: oauth/clients*,
oauth/personal-access-tokens* is there a way to remove them from the
endpoints published by passport?
You need to remove Passport::routes(); from AuthServiceProvider and manually put only required passport routes. I think you only need oauth/token route.
what exactly is "The-App" value for?
if you check oauth_access_tokens table it has name field. $user->createToken('Token Name')->accessToken; here the "Token Name" stored in name field.
How to use Laravel Passport with Password Grant Tokens?
To generate password grant token you have to store client_id and client_secret at app side (not recommended, check this ) and suppose if you have to reset the client_secret then the old version app stop working, these are the problems. To generate password grant token you have to call this api like you mention in step 3.
$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' => '',
],
]);
return json_decode((string) $response->getBody(), true);
Generate token from refresh_token
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => 'the-refresh-token',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);
You can look this https://laravel.com/docs/5.6/passport#implicit-grant-tokens too.
Tackling Question 5
Finally, there are a lot of endpoints that I get from passport that I don't think I will use for example: oauth/clients*, oauth/personal-access-tokens* is there a way to remove them from the endpoints published by passport?
Passport::routes($callback = null, array $options = []) takes an optional $callback function and optional $options argument.
The callback function takes a $router argument from which you can then choose which routes to install as shown below in your AuthServiceProvider.php that is enabling a more granular configuration:
Passport::routes(function ($router) {
$router->forAccessTokens();
$router->forPersonalAccessTokens();
$router->forTransientTokens();
});
Passport::tokensExpireIn(Carbon::now()->addMinutes(10));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(10));
This way we only create the passport routes that we need.
forAccessTokens(); enable us to create access tokens.
forPersonalAccessTokens(); enable us to create personal tokens although we will not use this in this article. Lastly,
forTransientTokens(); creates the route for refreshing tokens.
If you run php artisan route:list you can see the new endpoints installed by Laravel Passport.
| POST | oauth/token | \Laravel\Passport\Http\Controllers\AccessTokenController#issueToken
| POST | oauth/token/refresh | \Laravel\Passport\Http\Controllers\TransientTokenController#refresh

Laravel Passport "auth:api" middleware acts as "web, auth" middleware

I have set up the Laravel Passport package for Laravel 5.3 just as described in the official documentation (https://laravel.com/docs/5.3/passport#introduction).
I want the API to be consumed by a mobile application, so I am trying to implement Password Grant Tokens. I have created a password grant client, and the token request process...
$response = $http->post('http://my-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'my#email.com',
'password' => 'my-password',
'scope' => '',
],
]);
...Just works as expected, returning an access-token and a refresh-token for one of my users.
On the one hand,
php artisan route:list
Lists correct middleware for api/user URI: api,auth:api
And driver for api guard is correctly set to passport in config/auth.php.
Summing up, every step of the installation process has been done (https://laravel.com/docs/5.3/passport#installation).
Defaults contents of api.php:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');
The problem comes when I access to http://my-app.com/api/user, because it seems it is authenticating the request using the 'web' middleware, not the 'api'...
When I access, I am redirected to /login (login form) if the user was not logged in, and to /home if it was...
Any help would be really appreciated.
Thanks in advance.
Solved! Just for the record, the solution:
I was sending the request to http://my-app.com/api/user with HTTP Header wrong. I was sending:
Type: Authorization - Content: Bearer: $accessToken
...and the correct way was:
Type: Authorization - Content: Bearer $accessToken (without colon)
I never thought it could be a typo... Anyway, the error was not easy to detect because the redirection to the login form misleaded me from the beginning. I believe it was such an strange behaviour indeed...
The correct solution is removing redirectTo() from this file Authenticate middleware in app/http/middleware/Authenticate.php

Unable to use Laravel / Socialite with Lumen

I try to use the package Laravel\Socialite in my system in Lumen (5.1)
I added this in the config\services.php file :
<?php
//Socialite
'facebook' => [
'client_id' => '##################',
'client_secret' => '##################',
'redirect' => 'http://local.dev/admin/facebook/callback',
],
In bootstrap\app.php file :
class_alias(Laravel\Socialite\Facades\Socialite::class, 'Socialite');
$app->register(Laravel\Socialite\SocialiteServiceProvider::class);
Then I created a controller for the facebook authentication :
<?php
namespace App\Http\Controllers\Facebook;
use App\Http\Controllers\Controller;
use Laravel\Socialite\Contracts\Factory as Socialite;
class FacebookController extends Controller
{
public function redirectToProviderAdmin()
{
return Socialite::driver('facebook')->scopes(['manage_pages', 'publish_actions'])->redirect();
}
public function handleProviderCallbackAdmin()
{
$user = Socialite::driver('facebook')->user();
}
}
And in the routes.php :
$app->get('/admin/facebook/login', 'App\Http\Controllers\Facebook\FacebookController#redirectToProviderAdmin');
$app->get('/admin/facebook/callback', 'App\Http\Controllers\Facebook\FacebookController#handleProviderCallbackAdmin');
I just followed the documentation, changing according to my needs. When I go to page http://local.dev/admin/facebook/login, I get the following error :
Non-static method Laravel\Socialite\Contracts\Factory::driver() cannot be called statically, assuming $this from incompatible context
Indeed, according to the code, driver function must be instanciate.
EDIT : And if I try to instanciate this class, I get the following error :
Cannot instantiate interface Laravel\Socialite\Contracts\Factory
How do you make this module to work?
here's how that works in my case
in services.php file
'facebook' => [
'client_id' => '***************',
'client_secret' => '***************',
'redirect' => ""
],
i left redirect empty cause my site is multilingual (so, it fills in a bit later with sessions). if you use only one language, put there your callback absolute path. for example
"http://example.com:8000/my-callback/";
also check your config/app.php. in providers array
Laravel\Socialite\SocialiteServiceProvider::class,
in aliases array
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
my routes look like this
Route::get('facebook', 'Auth\AuthController#redirectToProvider');
Route::get('callback', 'Auth\AuthController#handleProviderCallback');
here's auth controllers methods. put in top
use Socialite;
//იობანი როტ
public function redirectToProvider(Request $request)
{
return Socialite::with('facebook')->redirect();
}
public function handleProviderCallback(Request $request)
{
//here you hadle input user data
$user = Socialite::with('facebook')->user();
}
my facebook app
giga.com:8000 is my localhost (so its the same localhost:8000)
as you can see in Valid OAuth redirect URI, you should put there your callback. in my case i use three urls cause i have three languages. in your case it should be just
http://your-domain-name.com:8000/callback
if you work on localhost, you should name your domain in config/services.php
mine look like this
'domain' => "your-domain.com",
after everything run these commands
php artisan cache:clear
php artisan view:clear
composer dump-autoload
restart your server, clear your browser cookies. hope it helps

Categories