Is There anyway to add or pass 1 more variable to findForPassport ?
In default laravel passport login, i only can pass 2 variable (username, password),but I want to pass 1 more variable and check in findForPassport if that user is belong other table or not .
From the link to passport issue #81 pointed by #Arun in OP:
There's probably a better way of doing this now but I extended the PassportServiceProvider and copied the registerAuthorizationServer function so that I could register my own grant type.
Swap out the provider in config\app.php with your new one:
'providers' => [
//Laravel\Passport\PassportServiceProvider::class,
App\Providers\PassportClientCredentialsServiceProvider::class,
Updated registerAuthorizationServer function that includes new grant option:
protected function registerAuthorizationServer()
{
parent::registerAuthorizationServer();
$this->app->singleton(AuthorizationServer::class, function () {
return tap($this->makeAuthorizationServer(), function ($server) {
/**
* #var $server AuthorizationServer
*/
$server->enableGrantType(
new ClientCredentialsGrant(), Passport::tokensExpireIn()
);
/** custom grant type */
$server->enableGrantType(
new PasswordOverrideGrant(
$this->app->make(UserRepository::class),
$this->app->make(RefreshTokenRepository::class)
), Passport::tokensExpireIn()
);
$server->enableGrantType(
$this->makeAuthCodeGrant(), Passport::tokensExpireIn()
);
$server->enableGrantType(
$this->makeRefreshTokenGrant(), Passport::tokensExpireIn()
);
$server->enableGrantType(
$this->makePasswordGrant(), Passport::tokensExpireIn()
);
$server->enableGrantType(
new PersonalAccessGrant(), new \DateInterval('P1Y')
);
});
});
}
PasswordOverrideGrant looks like this:
<?php
namespace App\Auth;
use App\User;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\RequestEvent;
use Psr\Http\Message\ServerRequestInterface;
class PasswordOverrideGrant extends PasswordGrant
{
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
$username = $this->getRequestParameter('username', $request);
if (is_null($username)) {
throw OAuthServerException::invalidRequest('username');
}
$custom_hash_token = $this->getRequestParameter('hash_token', $request);
if (is_null($custom_hash_token)) {
throw OAuthServerException::invalidRequest('identifier');
}
$credentials = [
'username' => $username,
'hash_token' => $custom_hash_token,
];
$user = User::where($credentials)->first();
if ($user instanceof User === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}
return $user;
}
public function getIdentifier()
{
return 'password_override';
}
}
I hope this answer can help to other.
If you want to add variable and pass this variable to findPassport function in User Authenticate model , you need to update 3 class in passport :
- UserRepositoryInterface in vendor\league\oauth2-server\src\Repositories\UserRepositoryInterface
- PasswordGrant in vendor\league\oauth2-server\src\Grant\PasswordGrant
- UserRepository in vendor\laravel\passport\src\Bridge\UserRepository
in the example code I will add parent variable and code will look like this
+in UserRepositoryInterface class
interface UserRepositoryInterface extends RepositoryInterface
{
/**
* Get a user entity.
*
* #param string $username
* #param string $password
* #param string $grantType The grant type used
* #param ClientEntityInterface $clientEntity
*
* #return UserEntityInterface
*/
public function getUserEntityByUserCredentials(
$username,
$password,
$parent, <------variable example
$grantType,
ClientEntityInterface $clientEntity
);
}
+in PasswordGrant class
class PasswordGrant extends AbstractGrant{
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
$username = $this->getRequestParameter('username', $request);
if (is_null($username)) {
throw OAuthServerException::invalidRequest('username');
}
$password = $this->getRequestParameter('password', $request);
if (is_null($password)) {
throw OAuthServerException::invalidRequest('password');
}
/**
* Get a user parent.
* varaible example
*/
$parent = $this->getRequestParameter('parent', $request);
if (is_null($parent)) {
throw OAuthServerException::invalidRequest('password');
}
$user = $this->userRepository->getUserEntityByUserCredentials(
$username,
$password,
$parent, <--- variable example get from request
$this->getIdentifier(),
$client
);
if ($user instanceof UserEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}
return $user;
} }
+in UserRepository class
class UserRepository implements UserRepositoryInterface
{
public function getUserEntityByUserCredentials($username, $password, $parent, $grantType, ClientEntityInterface $clientEntity)
/*add 1more parameter that implement from UserRepositoryInterface*/
{
$provider = config('auth.guards.api.provider');
if (is_null($model = config('auth.providers.'.$provider.'.model'))) {
throw new RuntimeException('Unable to determine authentication model from configuration.');
}
if (method_exists($model, 'findForPassport')) {
$user = (new $model)->findForPassport($username,$parent); <--- finally we pass parent variable to findForPassport here
} else {
$user = (new $model)->where('email', $username)->first();
}
if (! $user) {
return;
} elseif (method_exists($user, 'validateForPassportPasswordGrant')) {
if (! $user->validateForPassportPasswordGrant($password)) {
return;
}
} elseif (! $this->hasher->check($password, $user->getAuthPassword())) {
return;
}
return new User($user->getAuthIdentifier());
}
}
then u can get $parent value from parameter in findForPassport .but make sure you return value as eloquent User .if you want to join table , you can look my example code below
class User extends Authenticatable{
..........
public function findForPassport($identifier,$parent) {
$a = $this
->Join('role as r', 'r.user_id', '=', 'users.id')
->get();
return $a->where('name', $identifier)->where('role_id',$parent)->first();
}
}
Related
I am using Socialite for Google Authentication, i have an middleware for check that if user exist in my local database by their google_id, it can redirect to /home page otherwise it will redirect back to Google Authentication page, while doing this i am facing issue in middle and the error is:
Client error: POST https://www.googleapis.com/oauth2/v4/token resulted in a 400 Bad Request response: { "error": "invalid_request", "error_description": "Missing required parameter: code" }
Middleware
<?php
namespace App\Http\Middleware;
//use Socialite;
use App\GmailAccount;
use Closure;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Facades\Auth;
class GoogleAuth
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Socialite::driver('google')->stateless()->user();
$finduser = GmailAccount::where('google_id', $user->id)->first();
if($finduser){
return $next($request);
}
else{
return route('/logingoogle');
}
}
}
Routes
Route::group(['middleware' => 'App\Http\Middleware\GoogleAuth'], function()
{
Route::get('/home', 'HomeController#index')->name('home');
});
Route::get('/logingoogle', 'GoogleController#google_login');
Route::get('auth/google', 'GoogleController#redirectToGoogle');
Route::get('auth/google/callback', 'GoogleController#handleGoogleCallback');
HomeController
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('googleauth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
$user_id=Auth::user()->id;
$user_messages=DB::table('user_message')
->join('messages', 'user_message.message_id', '=', 'messages.id')
->where([
'user_message.user_id' => $user_id,
'user_message.placeholder' => 'inbox'
])
->select('messages.*', 'user_message.message_id', 'user_message.user_id','user_message.is_read')
->orderBy('messages.id', 'DESC')
->paginate(10);
return view('website_pages.home',compact('user_messages'));
}
}
GoogleController
class GoogleController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
public function google_login(){
return view('website_pages.login_google');
}
public function redirectToGoogle()
{
return Socialite::driver('google')->stateless()->redirect();
}
/**
* Create a new controller instance.
*
* #return void
*/
public function handleGoogleCallback()
{
$login_user_id = Auth::user()->id;
try {
$user = Socialite::driver('google')->stateless()->user();
$finduser = GmailAccount::where('google_id', $user->id)->first();
if($finduser){
return redirect('/home');
}
else{
$newUser = DB::table('gmail_accounts')->updateOrInsert(
[
'email' => $user->email,
],
[
'user_id' => $login_user_id,
'email' => $user->email,
'google_id'=> $user->id,
'remember_token'=> $user->token
]
);
if ($newUser){
return redirect('/home');
}
else{
return redirect()->back();
}
Auth::login($newUser, true);
}
} catch (Exception $e) {
dd($e->getMessage());
}
}
}
GmailAccount Model
class GmailAccount extends Model
{
protected $table = 'gmail_accounts';
protected $fillable = [
'email', 'password', 'google_id', 'user_id', 'remember_token'
];
public function user()
{
return $this->belongsTo('App\User');
}
}
Can you try this?
if($finduser) {
auth()->login($finduser), true);
}
else {
return redirect('/logingoogle');
}
And include these:
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Socialite;
Follow this which is a working example:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Socialite;
use App\User;
class LoginController extends Controller
{
use AuthenticatesUsers;
public function redirectToProvider()
{
return Socialite::driver('google')->redirect();
}
public function handleProviderCallback()
{
try {
$user = Socialite::driver('google')->user();
} catch (\Exception $e) {
return redirect('/login');
}
// check if they're an existing user
$existingUser = User::where('email', $user->email)->first();
if ($existingUser) {
// log them in
auth()->login($existingUser, true);
} else {
// create a new user
$newUser = new User;
$newUser->name = $user->name;
$newUser->email = $user->email;
$newUser->google_id = $user->id;
$newUser->avatar = $user->avatar;
$newUser->avatar_original = $user->avatar_original;
$newUser->lastlogin_at = \Carbon\Carbon::now();
$newUser->save();
auth()->login($newUser, true);
}
session(['user_name' => $user->name]);
session(['user_email' => $user->email]);
session(['user_avatar' => $user->avatar]);
return redirect()->to('/home');
}
}
Route::get('/redirect', 'Auth\LoginController#redirectToProvider');
Route::get('/callback', 'Auth\LoginController#handleProviderCallback');
This is the error I am getting the at moment:
Type error: Argument 1 passed to Illuminate\Database\Eloquent\Relations\BelongsToMany::save()
must be an instance of Illuminate\Database\Eloquent\Model,
integer given,
called in /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php on line 814
The create user method, where I call the giveRole() method:
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
// On registration user will be given the default role of user
$user->giveRole();
$verifyUser = VerifyUser::create([
'user_id' => $user->id,
'token' => str_random(40)
]);
Mail::to($user->email)->send(new VerifyMail($user));
return $user;
}
HasPermissionsTrait:
<?php
namespace App\App\Permissions;
use App\{Role, Permission};
/**
*
*/
trait HasPermissionsTrait
{
public function giveRole($role = 'user')
{
$role = \DB::table('roles')->where('name', '=', $role)->first();
$this->roles()->saveMany([$role->id]);
return $this;
}
public function givePermission(...$permissions)
{
$permissions = $this->getPermissions(\array_flatten($permissions));
if($permissions === null)
return $this;
$this->permissions()->saveMany($permissions);
return $this;
}
public function widrawPermission(...$permissions)
{
$permissions = $this->getPermissions(\array_flatten($permissions));
$this->permissions()->detach($permissions);
return $this;
}
public function updatePermissions(...$permissions)
{
$this->permissions()->detach();
return $this->givePermission($permissions);
}
public function hasRole(...$roles)
{
foreach ($roles as $role) {
if($this->roles->contains('name', $role))
return true;
}
return false;
}
public function hasPermissionTo($permission)
{
return $this->hasPermissionThroughRole($permission) || $this->hasPermission($permission);
}
protected function hasPermission($permission)
{
return (bool) $this->permissions->where('name', $permission->name)->count();
}
protected function hasPermissionThroughRole($permission)
{
foreach ($permission->roles as $role) {
if($this->role->contains($role))
return true;
}
return false;
}
protected function getPermissions(array $permissions)
{
return Permissions::whereIn('name', $permissions)->get();
}
public function roles()
{
return $this->belongsToMany(Role::class, 'users_roles', 'user_id', 'role_id');
}
public function permissions()
{
return $this->belongsToMany(Permissions::class, 'users_permissions');
}
}
Role model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function permissions()
{
return $this->belongsToMany(Permissions::class, 'roles_permissions');
}
}
User model:
namespace App;
use App\App\Permissions\HasPermissionsTrait;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable, HasPermissionsTrait;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function clinic()
{
return $this->hasOne(Clinic::class, 'owner_id');
}
public function files()
{
return $this->hasMany('App/Media');
}
public function verifyUser()
{
return $this->hasOne('App\VerifyUser');
}
}
What am I doing wrong here?
Have you tried passing in the role model instead of the id? Also, on a separate note, it looks as if you might as well just call save as you are not actually ever utilizing an array in this instance.
trait HasPermissionsTrait
{
public function giveRole($role = 'user')
{
$role = \DB::table('roles')->where('name', '=', $role)->first();
$this->roles()->saveMany([$role]);
return $this;
}
}
saveMany calls save:
public function saveMany($models, array $joinings = [])
{
foreach ($models as $key => $model) {
$this->save($model, (array) Arr::get($joinings, $key), false);
}
$this->touchIfTouching();
return $models;
}
and save has typecasted Model, not int:
/**
* Save a new model and attach it to the parent model.
*
* #param \Illuminate\Database\Eloquent\Model $model
* #param array $joining
* #param bool $touch
* #return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model, array $joining = [], $touch = true)
{
$model->save(['touch' => false]);
$this->attach($model->getKey(), $joining, $touch);
return $model;
}
We are working on two laravel projects one for front end laravel and for backend api. I followed tutorials on connecting this two projects but make use of guzzlehttp. However I am getting undefined index password. I already dd the user['data'] in getUsers method and gettign the correct password. Can any one help me on this.
ApiUserProvider
<?php
namespace App\Auth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
class ApiUserProvider implements UserProvider
{
public function retrieveByCredentials(array $credentials)
{
$user = $this->getUserByUsername($credentials['username']);
return $this->getApiUser($user);
}
public function retrieveById($identifier)
{
$user = $this->getUserById($identifier);
return $this->getApiUser($user);
}
public function validateCredentials(UserContract $user, array $credentials)
{
return $user->getAuthPassword() == bcrypt($credentials['password']);
}
protected function getApiUser($user)
{
if ($user !== null) {
return new ApiUser($user);
}
}
protected function getUsers()
{
$client = new Client(['base_uri' => 'http://127.0.0.1:80/api.kourse/public/api/v1/']);
$response1 = $client->request('POST', 'oauth/access_token', [
'form_params' => [
'client_id' => 'id1',
'client_secret' => 'secret1',
'grant_type' => 'password',
'username' => 'email#yahoo',
'password' => 'password'
]
]);
$location = json_decode($response1->getBody(), true);
$token = $location['access_token'];
// Send a request to https://foo.com/api/test
$response2 = $client->request('GET', 'users/self', [
'headers' => [
'Authorization' => 'Bearer '. $token
]
]);
$user = json_decode($response2->getBody(), true);
return $user['data'];
}
protected function getUserById($id)
{
$user = [];
if($this->getUsers()['email'] == $id){
$user['id'] = $id;
}
dd($user);
return $user ?: null;
}
protected function getUserByUsername($username)
{
$user = [];
if($this->getUsers()['email'] == $username){
$user['email'] = $username;
}
return $user ?: null;
}
// The methods below need to be defined because of the Authenticatable contract
// but need no implementation for 'Auth::attempt' to work and can be implemented
// if you need their functionality
public function retrieveByToken($identifier, $token) { }
public function updateRememberToken(UserContract $user, $token) { }
}
ApiUser
namespace App\Auth;
use Illuminate\Auth\GenericUser;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
class ApiUser extends GenericUser implements UserContract
{
public function getAuthIdentifier()
{
return $this->attributes['id'];
}
}
UserController
public function login(Request $request)
{
$email = $request->email;
$password = bcrypt($request->password);
if (Auth::attempt(['username' => $email, 'password' => $password])) {
return "hello";
}
}
error
AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any application authentication / authorization services.
*
* #param \Illuminate\Contracts\Auth\Access\Gate $gate
* #return void
*/
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
Auth::provider('api', function($app, array $config) {
return new ApiUserProvider($config['model']);
});
}
}
My best guess would be to open then User model and if you have:
protected $hidden = [
'password',
'remember_token',
];
to make it an empty array, like this protected $hidden = [];. I guess this might work because when you make new ApiUser return new ApiUser($user); it's converting the User object to array and it removes the password attribute because of the $hidden property.
As validatePassword() receives the user's password?
I not know how validatePassword() get password user.
$password - Getting here is assigned to the user's password?
<?php
namespace app\models;
class User extends \yii\base\Object implements \yii\web\IdentityInterface
{
public $id;
public $username;
public $password;
public $authKey;
public $accessToken;
private static $users = [
'100' => [
'id' => '100',
'username' => 'admin',
'password' => 'admin',
'authKey' => 'test100key',
'accessToken' => '100-token',
],
'101' => [
'id' => '101',
'username' => 'demo',
'password' => 'demo',
'authKey' => 'test101key',
'accessToken' => '101-token',
],
];
/**
* #inheritdoc
*/
public static function findIdentity($id)
{
return isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
}
/**
* #inheritdoc
*/
public static function findIdentityByAccessToken($token, $type = null)
{
foreach (self::$users as $user) {
if ($user['accessToken'] === $token) {
return new static($user);
}
}
return null;
}
/**
* Finds user by username
*
* #param string $username
* #return static|null
*/
public static function findByUsername($username)
{
foreach (self::$users as $user) {
if (strcasecmp($user['username'], $username) === 0) {
return new static($user);
}
}
return null;
}
/**
* #inheritdoc
*/
public function getId()
{
return $this->id;
}
/**
* #inheritdoc
*/
public function getAuthKey()
{
return $this->authKey;
}
/**
* #inheritdoc
*/
public function validateAuthKey($authKey)
{
Yii::info('Пароль validateAuthKey ----'.$authKey);
return $this->authKey === $authKey;
}
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
return $this->password === $password;
}
}
THe code you show is the User.php model code in basic template application.
as you can see in site controller the action login call $model->login()
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) { // *** here
return $this->goBack();
}
return $this->render('login', [
'model' => $model,
]);
}
($model is obtained by new LoginForm();).
in $model->login (alias in LoginForm->login()) you have this code
/**
* Logs in a user using the provided username and password.
* #return boolean whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
}
return false;
}
this code perform the login
I am trying out Laravel 5.1's queue, I am having a problem working with $event object in its listener.
AuthController.php
public function postGenerateResetToken()
{
try
{
$admin = Admin::where( 'email', '=', Input::get( 'email' ) )->firstOrFail();
$token = Bus::dispatch( new GeneratePasswordResetToken( $admin ) );
event( new PasswordResetTokenWasGenerated( $admin, $token ) );
return success();
}
catch( ModelNotFoundException $exception )
{
return fail();
}
}
PasswordResetTokenWasGenerated.php
class PasswordResetTokenWasGenerated extends Event
{
use SerializesModels;
public function __construct( $admin, $token )
{
$this->admin = $admin;
$this->token = $token;
}
public function broadcastOn()
{
return [];
}
}
SendForgottenPasswordEmail.php
class SendForgottenPasswordEmail implements ShouldQueue
{
public function __construct()
{
//
}
public function handle(PasswordResetTokenWasGenerated $event)
{
$data = [
'admin' => $event->admin,
'token' => $event->token
];
Mail::send( 'emails.forgotten-password', $data, function( $message ) use ( $event )
{
$message->subject( 'Forgotten password' );
$message->to( $event->admin->email );
});
}
}
Using $event->admin in handler results in Undefined property: PasswordResetTokenWasGenerated::$admin
But, this error only occurs when I implement ShouldQueue interface on Listener. It works just fine without the interface.
The queue driver is set to sync.
I know this is because of the queue, but isn't it supposed to work the way I want it to work?
You should declare your admin and token as public before setting them:
class PasswordResetTokenWasGenerated extends Event
{
use SerializesModels;
public $admin;
public $token;
public function __construct( $admin, $token )
{
$this->admin = $admin;
$this->token = $token;
}
public function broadcastOn()
{
return [];
}
}
After that you should be able to access those properties in your Listener.