I have a problem with laravel 8 and jwt token here is a little background
I have two laravel 8 api with two databases on two different servers which I will call app 1 and app 2
and I must be able to auto connect with user 1 who has id 5 on app 1 and this same user exists but he has id 2 on app 2 but the reverse is not possible a user of app 2 cannot this connect to app 1 so sso is not possible unless I'm wrong? .
The problem is that laravel, but automatically id of the current user id 5 in the subject 'sub' of the token and I cannot overload this when I would have to give it id 2 in the ' sub 'of the token,
I manage to customize the token by adding email for example and I get the id of user 2 in the database of app 2, but each time laravel pass id of user 1, would anyone have an idea
Here is my code here at the login I will look in the DB for the user according to his email which is unique and I put in session the information that I retrieve and launch the autoload function which creates a new token
$app2User = DB::connection('mysql2')->table("users")->where('email', '=', $request->email)->pluck('id');
if (count($app2User) > 0) {
session(['app2UserId' => $app2User[0], 'app2UserEmail' => $request->email]);
$this->autoload();
} else {
log::info('is null');
}
session(['optisiloUserEmail' => $request->email]);
return $this->createNewToken($token);
Autoload function in which I put the token create in app2UserToken session to transmit it to my createNewToken function which sends me a json with the token of the current user and access_token to send to app2 for this connection
public function autoload()
{
$id = session('optisiloUserId');
$email = session('optisiloUserEmail');
$payload = JWTFactory::sub($id)->email($email)->make();
$token = JWTAuth::encode($payload);
session(['app2UserToken' => $token->get()]);
}
protected function createNewToken($token)
{
return response()->json([
'token' => $token,
'access_token' => session('optisiloUserToken'),
'token_type' => 'bearer',
'expires_in' => Auth::factory()->getTTL() * 60,
'user' => User::where('id', Auth::id())
]);
}
}
I tried this in the User model but suddenly it works on app 2 but no longer on app1 because the sub takes user id from app 2
it is the function $this->getKey() which retrieves the primaryKey or id of the current user
public function getJWTIdentifier()
{
$id = $this->getKey();
$res = User::where('id', $id)->pluck('id_user_app2')[0];
if ($res === null) {
return $this->getKey();
} else {
return $res;
}
}
how could we override the token's sub or or tell it who puts id 5 in the token and id 2 in access_token?
Related
I'm developing a webpage with Laravel 8 and I have issues with fetching a patron details by id from Patreon API. Here is my use case.
I’ve added "Login with Patreon" option to my webpage, and it works well. When someone login with Patreon successfully, I store her/his Patreon id and set remember token to login the member automatically when she/he visits my page next time.
The first login process is fine. The problem occurs when my Patron visits my page next time. Because I want to check whether I received any payment before I let she/he see all content. That’s why I need to get my patron details from a middleware. To do that I tried:
fetch_user() returns my account details instead of logged-in user.
fetch_user() with the access token that returns from Patreon when
someone login, returns unauthorized.
fetch_member_details() doesn’t work with the id I passed, which is an
integer like 5484646 because it requires a very long string like
55153fds-f45fd5sfs-fds42ds, I don't know what it's.
fetch_page_of_members_from_campaign() and fetch_member_details()
together to get the proper ID, but it takes ages to get data, which
is unacceptable.
So, how can it be done?
https://further-reading.net/2020/06/getting-names-of-your-patreon-patrons-by-tier/
This might be useful. I believe, there is not a direct single API for this, but you can -
First fetch all campaigns/tiers data
And then fetch patrons for each campaign/tier
I like to answer my question for those who need some help.
First of all, I use the official PHP package by Patreon
I've created a middleware to check if the user should be authorized again. In order to prevent the same process every single time, I set timeout to users table and check if it still has time to expire. If it does, no need to do anything. Of course, this is my use case, but without that explanation, some parts of the code can be nonsense to you.
// App\Http\Middleware\AuthenticateMember.php
public function handle(Request $request, Closure $next)
{
if (!Auth::check()) {
return $next($request);
}
if (Carbon::parse(Auth::user()->timeout)->isFuture()) {
return $next($request);
}
$this->refreshCredentials();
return $next($request);
}
If "timeout" isn't in the future, refreshCredentials method will be called. This is a method, which will trigger binding AuthGatewayContract to the service container.
// App\Trait\Users.php
public function refreshCredentials()
{
$gateway = App::make('App\Services\AuthGatewaysContract');
$gateway->ensureUserStillAuthenticated();
}
public function handleUserRecord($user)
{
return User::updateOrCreate([
'email' => $user['email']
], $user);
}
public function attemptToLogin($user, $remember = true)
{
Auth::login($user, $remember);
event(new Registered($user));
}
This is how the binding works:
// App\Providers\AppServiceProvider.php
public function register()
{
$this->app->singleton(AuthGatewaysContract::class, function () {
$routeParts = explode('/', url()->current());
$gateway = array_pop($routeParts); // this is how I know which "Login with ..." button is clicked.
$isGateway = Gateway::where('name', $gateway)->first();
$gateway = $isGateway ? ucfirst($gateway) : ucfirst(Auth::user()->gateway->name);
$class = "\App\Services\AuthGateways\\$gateway";
return new $class();
});
}
So Patreon.php is active gateway now, and ensureUserStillAuthenticated can be called:
// App\Services\AuthGateways\Patreon.php
public function ensureUserStillAuthenticated()
{
$this->authenticate([
'access_token' => Auth::user()->access_token,
'refresh_token' => Auth::user()->refresh_token,
]);
}
private function authenticate($tokens)
{
$patron = $this->fetchUserFromGateway($tokens);
$user = $this->handleResponseData($patron, $tokens);
$user = $this->handleUserRecord($user);
return $this->attemptToLogin($user);
}
private function fetchUserFromGateway($tokens)
{
// This is the only function that communicate with Patreon-php package.
$api_client = new API($tokens['access_token']);
return $api_client->fetch_user();
}
private function handleResponseData($data, $tokens)
{
return [
'name' => $data['data']['attributes']['full_name'],
'email' => $data['data']['attributes']['email'],
'password' => Hash::make(Str::random(24)),
'role_id' => $this->assignRoleId($data),
'payment_id' => Payment::where('name', 'patreon')->first()->id,
'gateway_id' => Gateway::where('name', 'patreon')->first()->id,
'access_token' => $tokens['access_token'],
'refresh_token' => $tokens['refresh_token'],
'timeout' => Carbon::today()->addMonth()->toDateString()
];
}
I am pulling User Information from an external site with external API. I have completed the user login route on the Laravel and I get the data from the controller file. There is no problem in terms of pulling and displaying data from an external user API link.
How to do token and session operation like regular Laravel user to the user logged in with external API without the database. Note that I can use the same token part of the user API token available
In addition, I don't want to transfer the information by assigning session between the controller each time the user was login. How do I assign tokens in all transactions after user login?
It comes to these controls via post method from login screen
public function loginData(Request $request)
{
$password = $request->password;
$email = $request->email;
$apiman = "Bearer {$this->accesstokenApi()}";
$client = new Client();
$response = $client->post('https://testapi.com/api/v3/Profile', [
'headers' =>
[
'cache-control' => 'no-cache',
'authorization' => $apiman,
'content-type' => 'application/json'
],
'json' =>
[
'Email' => $email,
'Password' => $password
],
]);
$data = json_decode((string) $response->getBody(), true);
if ($data['ResponseType']=="Ok") {
session()->put('token', $data);
return redirect('/user-detail');
} else {
return response()->json([
'success' => false,
'message' => 'Invalid Email or Password',
], 401);
}
}
User logged in OK . After that, what token should the machine give, or where can the session be given to that user in one place? Besides, if the user is logged in, how do I get him to see the home page instead of showing the login form again, just like in Laravel login processes ?
Maybe you can create new middleware that will check if there is a token in the session
Here is the example that you can use and adapt it based on your needs.
namespace App\Http\Middleware;
use Closure;
class Myauth
{
public function handle($request, Closure $next, $guard = null)
{
if(session()->has('token')) {
return $next($request);
} else {
return response('Unauthorized.', 401);
//OR return redirect()->guest('/');
}
}
}
I am trying to add social authentication to a Laravel 5.8 API project using socialite.
When trying to handle a social provide callback, the ArgumentCountError is thrown here
Too few arguments to function App\Http\Controllers\SocialAuthController::handleProviderCallback(), 0 passed and exactly 1 expected
The error is referring to the very first line of this code block
public function handleProviderCallback($provider)
{
// retrieve social user info
$socialUser = Socialite::driver($provider)->stateless()->user();
// check if social user provider record is stored
$userSocialAccount = SocialAccount::where('provider_id', $socialUser->id)->where('provider_name', $provider)->first();
if ($userSocialAccount) {
// retrieve the user from users store
$user = User::find($userSocialAccount->user_id);
// assign access token to user
$token = $user->createToken('Pramopro')->accessToken;
// return access token & user data
return response()->json([
'token' => $token,
'user' => (new UserResource($user))
]);
} else {
// store the new user record
$user = User::create([
'name' => $socialUser->name,
'username' => $socialUser->email,
'email_verified_at' => now()
]);
...
// assign passport token to user
$token = $user->createToken('******')->accessToken;
// return response
return response()->json(['token' => $token]);
}
}
Below is how I have set up other code. Frist in env I added
GOOGLE_CLIENT_ID=******
GOOGLE_CLIENT_SECRET=*******
GOOGLE_CALLBACK_URL=https://staging.appdomain.com/api/v1/user
Then modified web.php
Auth::routes(['verify' => true]);
Route::get('/auth/{provider}', 'SocialAuthController#redirectToProvider');
Route::get('/auth/{provider}/callback', 'SocialAuthController#handleProviderCallback');
Lastly in the google app, I added the uri path where users will be redirected to after successful authentication
https://staging.appdomain.com/api/v1/user
How do I fix this?
The callback uri that user should be redirected to after successful authentication was apparently not being cached. So running php artisan route:cache fixed it.
I have a Laravel application that uses Passport authentication.
Login
public function authenticate(Request $request)
{
$params = [
'grant_type' => 'password',
'client_id' => 1,
'client_secret' => "secret",
'username' => request('username'),
'password' => request('password'),
'active' => 1,
'scope' => '*'
];
$request->request->add($params);
// verify the credentials and create a token for the user
$proxy = Request::create('oauth/token', 'POST');
return Route::dispatch($proxy);
}
I have settled the expiration on AuthServiceProvider:
Passport::routes(function ($router) {
$router->forAccessTokens();
});
Passport::tokensExpireIn(now()->addMinute(1));
Passport::refreshTokensExpireIn(now()->addDays(30));
It works but after 1 minute the token expires. I want a different expiration date for token depending on where I'm trying to make login because I have a website, desktop app and an Android app.
For example:
web app: 8 hours
desktop app: 1 Year
android app: 5 months
I was thinking send me from where I'm trying to make the login, but is that a good way? Are there any other possible ways to do it?
For now I have tried this:
-) deleted From AuthServiceProvider:
Passport::tokensExpireIn(now()->addMinute(1));
And added in Login function:
if (request('from') == 'something') {
Passport::tokensExpireIn(now()->addYears(1));
} else {
Passport::tokensExpireIn(now()->addHours(8));
}
$proxy = Request::create('oauth/token', 'POST');
You can get help from below link please find
For getting user agent detail and apply condition base on agent
for example you can use code like below
if ( Agent::isDesktop() ) {
Passport::tokensExpireIn(now()->addYears(1));
} else if(Agent::isMobile()){
Passport::tokensExpireIn(now()->addMonth(5));
}else{
Passport::tokensExpireIn(now()->addHours(8));
}
I have a client API where the first API is loginAPI URL when it triggers the token ID will be generated and it will be activated only for 20mins, and the token ID has to pass in the main API URL has in headers. After 20mins the loginAPI Url has to trigger once again to get the fresh token id every time.
So I am new this kind of issue so can anyone help me giving ideas/suggestions to run the API.
#Ritesh : Please follow below steps
Create on device table in your system in that table you manage your user id and device id and token id, token time.
When you call main api then token time is get update with respective of that token id
Match current time and token updated time. If time interval is greater than 20 minute then update token id
function checkValidSession($userToken)
{
if(trim($userToken)=="")
{
$array = array('error_code' => 301, 'message' => 'Token is missing','status'=>FAIL);
return $array;
}else
{
$CI = & get_instance();
$result = $CI->db->select("*")->from('tbl_devices')->where("token",$userToken)->get()->row();
if(!$result)
{
$array = array('error_code' => 302, 'message' => 'Invalid token','status'=>FAIL);
return $array;
}
else
{
$updated_date = $result->updated_date;
$tokenTime = strtotime($updated_date);
$currTime = time();
$minutes = round(abs($currTime - $tokenTime) / 60,2);
if($minutes>20)
{
Run login webservice again
}
else
{
$CI->db->update('tbl_devices',array('updated_date'=>date('Y-m-d H:i:s')),array('token'=>$userToken));
$array = array('error_code' => 0,'data'=>$result,'message' => 'Valid token','status'=>SUCCESS);
return $array;
}
}
}
}
Following are table schema
http://prntscr.com/khz8wz