I'm trying to combine Laravel project and Nova admin panel. In the project I need to reset Nova password, But there is a FatalThrowableError error in PasswordBroker.php file.
This is the error
This is my PasswordBroker.php file.
<?php
namespace Illuminate\Auth\Passwords;
use Closure;
use Illuminate\Support\Arr;
use UnexpectedValueException;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\PasswordBroker as PasswordBrokerContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class PasswordBroker implements PasswordBrokerContract
{
protected $tokens;
protected $users;
protected $passwordValidator;
public function __construct(TokenRepositoryInterface $tokens,
UserProvider $users)
{
$this->users = $users;
$this->tokens = $tokens;
}
public function sendResetLink(array $credentials)
{
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
return static::RESET_LINK_SENT;
}
public function reset(array $credentials, Closure $callback)
{
$user = $this->validateReset($credentials);
if (! $user instanceof CanResetPasswordContract) {
return $user;
}
$password = $credentials['password'];
$callback($user, $password);
$this->tokens->delete($user);
return static::PASSWORD_RESET;
}
protected function validateReset(array $credentials)
{
if (is_null($user = $this->getUser($credentials))) {
return static::INVALID_USER;
}
if (! $this->validateNewPassword($credentials)) {
return static::INVALID_PASSWORD;
}
if (! $this->tokens->exists($user, $credentials['token'])) {
return static::INVALID_TOKEN;
}
return $user;
}
public function validator(Closure $callback)
{
$this->passwordValidator = $callback;
}
public function validateNewPassword(array $credentials)
{
if (isset($this->passwordValidator)) {
list($password, $confirm) = [
$credentials['password'],
$credentials['password_confirmation'],
];
return call_user_func(
$this->passwordValidator, $credentials
) && $password === $confirm;
}
return $this->validatePasswordWithDefaults($credentials);
}
protected function validatePasswordWithDefaults(array $credentials)
{
list($password, $confirm) = [
$credentials['password'],
$credentials['password_confirmation'],
];
return $password === $confirm && mb_strlen($password) >= 6;
}
public function getUser(array $credentials)
{
$credentials = Arr::except($credentials, ['token']);
$user = $this->users->retrieveByCredentials($credentials);
if ($user && ! $user instanceof CanResetPasswordContract) {
throw new UnexpectedValueException('User must implement CanResetPassword interface.');
}
return $user;
}
public function createToken(CanResetPasswordContract $user)
{
return $this->tokens->create($user);
}
public function deleteToken(CanResetPasswordContract $user)
{
$this->tokens->delete($user);
}
public function tokenExists(CanResetPasswordContract $user, $token)
{
return $this->tokens->exists($user, $token);
}
public function getRepository()
{
return $this->tokens;
}
}
Here I want to reset current password using email. Does anyone have a idea to fix this issue ?
Related
I want to put an authenticator in my API, this anthenticator have to check if the ID given in the headers is in my database. The problem is that I don't understand how this work.
I have read the docs but keep don't understand. (Maybe because of my english).
My API don't need login and password, I just want him to check at every request.
For now I have an error :
Uncaught PHP Exception UnexpectedValueException: "The App\Security\AppAuthenticator::getUser() method must return a UserInterface. You returned boolean
But i'm sure that I have a lot of another problem in my code.
There is my AppAuthenticator :
class AppAuthenticator extends AbstractGuardAuthenticator
{
public function supports(Request $request)
{
return true;
}
public function getCredentials(Request $request)
{
return [
'token' => $request->headers->has('DDMDL-AUTH-TOKEN'),
];
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$apiToken = $credentials['token'];
if (null === $apiToken) {
return;
}
require __DIR__ . DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR . "Entity" . DIRECTORY_SEPARATOR . "sqlconnect.php";
$auth = 'SELECT ID FROM MOBILEDDMDL WHERE ID = :idtel ';
$prepauth = $pdo->prepare($auth);
$prepauth->execute(array(':idtel' => $apiToken));
$user = $prepauth->fetch();
if ($user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Pas autorisé');
}
return $user;
//return $this->em->getRepository(User::class)->findOneBy(['apiToken' => $apiToken]);
}
public function checkCredentials($credentials, UserInterface $user)
{
// todo
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// todo
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
}
public function start(Request $request, AuthenticationException $authException = null)
{
// todo
}
public function supportsRememberMe()
{
// todo
}
}
getUser() want you to return a UserInterface instead instead of your boolean. I believe your User::class extends UserInterface so the commented line was the correct variable to return:
//return $this->em->getRepository(User::class)->findOneBy(['apiToken' => $apiToken]);
My getUser looks like :
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
if(strpos($credentials['identifiant'], '#') !== false){
$user = $this->entityManager->getRepository(Utilisateur::class)->findOneBy(['email' => $credentials['identifiant']]);
} else {
$user = $this->entityManager->getRepository(Utilisateur::class)->findOneBy(['identifiant' => $credentials['identifiant']]);
}
if (!$user) {
throw new CustomUserMessageAuthenticationException('Aucun utilisateur trouvé avec ce nom de compte');
}
return $user;
}
checkCredential is the method which verify the password or whatever and this method will return true of false (if the user connexion is ok or not)
My checkCredential looks like :
public function checkCredentials($credentials, UserInterface $user)
{
$passOk1 = password_verify($credentials['password'], $user->getMdp());
$passOk = md5($credentials['password']) === $user->getMdp();
$credential = false;
if($passOk || $passOk1) {
$user->setDerniereConnexion(new DateTime(date('Y-m-d H:i:s')));
$this->entityManager->persist($user);
$this->entityManager->flush();
$this->entityManager->clear();
$credential = true;
}
return $credential;
}
So i created a controller for authentication with 2 methods (token() / native)_). Im using fractal transformer to return response. The token method works fine for me, but the loginAndroid() returns
"Call to a member function createData() on null" error.
Any help? Thank you.
class AuthController extends RestController
{
protected $transformer = UserTransformers::Class;
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login', 'loginAndroid']]);
}
public function login(Request $request)
{
$credentials = $request->only(['username', 'password']);
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
public function loginAndroid(Request $request)
{
$credentials = $request->only(['username', 'password']);
if (Auth::attempt($credentials)) {
//$user = Auth::user()->with(['employees']);
$userdata = User::with(['employees', 'employees.role', 'employees.branch'])->find(Auth::id());
//$success['token'] = $user->createToken('MyApp')->accessToken;
//return response()->json($userdata, 200);
//return $userdata;
$response = $this->generateItem($userdata);
return $this->sendResponse($response, 201);
} else {
return response()->json('gagal', 401);
}
}
}
this is my restcontroller
abstract class RestController extends Controller
{
protected $manager;
protected $transformer;
public function __construct()
{
$this->manager = new Manager();
}
protected function generateItem($model, $transformer = null)
{
if (!is_null($transformer)) {
return new Item($model, new $transformer);
}
return new Item($model, new $this->transformer);
}
protected function generateCollection($model, $transformer = null)
{
if (!is_null($transformer)) {
return new Collection($model, new $transformer);
}
return new Collection($model, new $this->transformer);
}
protected function sendResponse(ResourceInterface $data, $status = 200)
{
return response()->json(
$this->manager->createData($data)->toArray(),
$status
);
}
protected function sendNotFoundResponse($status)
{
return response()->json($status, 404);
}
protected function sendIseResponse($status)
{
return response()->json($status, 500);
}
}
It looks like your sendResponse() method depends on $this->manager. However, $this->manager gets set in RestController::__construct() and you've overridden the __construct() method in your AuthController::__construct(). So, in order to have $this->manager available, you should call the parent constructor from your AuthController, like this:
class AuthController extends RestController
{
protected $transformer = UserTransformers::Class;
public function __construct()
{
parent::__construct(); // call the parent constructor where
// $this->manager gets initialized
$this->middleware('auth:api', ['except' => ['login', 'loginAndroid']]);
}
... etc
I'm trying to do:
when user authorized go to home page
when user not authorized go to Login page
but now when I put (correct) user email and password that always refresh login page and doesn't log in into system.
User(ActiveRecord)
class User extends ActiveRecord implements IdentityInterface
{
public function setPassword($user_password)
{
$this->password = sha1($user_password);
}
public function validatePassword($user_password)
{
return $this->user_password === sha1($user_password);
}
public static function findIdentity($id)
{
return self::findOne($id);
}
public static function findIdentityByAccessToken($token, $type = null)
{
}
public function getId()
{
return $this->user_id;
}
public function getAuthKey()
{
}
public function validateAuthKey($authKey)
{
}
}
Login Model:
class Login extends Model
{
public $user_email;
public $user_password;
public function rules()
{
return [
[['user_email', 'user_password'],'required'],
['user_email','email'],
['user_password','validatePassword']
];
}
public function validatePassword($attribute,$params)
{
if(!$this->hasErrors())
{
$user = $this->getUser();
if(!$user || !$user->validatePassword($this->user_password))
{
$this->addError($attribute, 'Пароль или пользователь введенны не верно');
}
}
}
public function getUser()
{
return User::findOne(['user_email'=>$this->user_email]);
}
}
?>
SiteController(only login function)
public function actionLogin()
{
if(!Yii::$app->user->isGuest)
{
return $this->goHome();
}
else {
$login_model = new Login();
return $this->render('login',['login_model'=>$login_model]);
}
}
Putting username and password is not enough you should also perform a login
public function actionLogin()
{
if(!Yii::$app->user->isGuest)
{
return $this->goHome();
}
if ($model->load(Yii::$app->getRequest()->post())) {
// you should perform login
\Yii::$app->getUser()->login($model->user, $this->rememberMe ? $model->module->rememberFor : 0);
return $this->goBack();
}
else {
$login_model = new Login();
return $this->render('login',['login_model'=>$login_model]);
}
}
I write a custom user provider to validate my user in a external API on external server.
CustomUserProvider:
<?php
namespace App\Auth;
use Illuminate\Auth\GenericUser;
use Illuminate\Contracts\Auth\Authenticatable as Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Psr\Http\Message\ResponseInterface;
use Illuminate\Http\Request;
use App\XXX\Facades\XXXX;
use Log;
class CustomUserProvider implements UserProvider {
public $errors = [];
public function getErrorMessages ( ) {
return $this->errors;
}
public function retrieveById($identifier)
{
// TODO: Implement retrieveById() method.
}
public function retrieveByToken($identifier, $token)
{
// TODO: Implement retrieveByToken() method.
return null;
}
public function updateRememberToken(Authenticatable $user, $token)
{
// TODO: Implement updateRememberToken() method.
}
public function retrieveByCredentials(array $credentials)
{
$response =APIService::get( 'GET', $credentials, 'tokens');
if ( array_key_exists('code', $response) ) {
/* Exception o Credentials Error */
$this->errors = $response;
return null;
} else {
$apiCredentials = json_decode( $response->getBody(), true );
if( $apiCredentials ) {
$attributes = array (
'id' => 1,
'name' => 'Name',
'lastname' => 'LastName',
'email' => 'email#email.com'
);
$user = new GenericUser( $attributes );
return $user;
}
}
return null;
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
if( $user->email == $credentials['email'] ) {
return true;
}
return false;
}
}
The problem is that the user if logged fine, but in the next request Auth::user() returns false. Debugging in the middleware Auth:user() is returning false too.
I'm a new user of Slim framework, I've a simple Slim 3 application, with sign in and sign up validation. But I'm not really sure if this is the right/best way to set errors and check if user is logged in -In order to redirect it to his account if session user.id exists.
I used a middleware: AuthMiddleware which includes:
class AuthMiddleware
{
protected $container;
public function __construct($container)
{
$this->container = $container;
}
public function __invoke($request, $response, $next)
{
if (isset($_SESSION['user.id']) && !empty($_SESSION['user.id'])) {
return $response->withRedirect($this->container->router->pathFor('user.index'));
}
$twig = $this->container->view->getEnvironment();
if (isset($_SESSION['validation'])) {
$twig->addGlobal('errors', $_SESSION['validation']['errors']);
$twig->addGlobal('values', $_SESSION['validation']['values']);
unset($_SESSION['validation']);
}
if (isset($_SESSION['auth.signup.success'])) {
$twig->addGlobal('auth_signup_success', $_SESSION['auth.signup.success']);
unset($_SESSION['auth.signup.success']);
}
if (isset($_SESSION['auth.signin.failed'])) {
$twig->addGlobal('auth_signin_failed', $_SESSION['auth.signin.failed']);
unset($_SESSION['auth.signin.failed']);
}
$response = $next($request, $response);
return $response;
}
}
And I used Twig for my views.
Session validation assigned in the validator.php which includes:
class Validator
{
protected $errors = [];
protected $values = [];
public function validate($request, $rules)
{
foreach ($rules as $field => $rule) {
$this->values[$field] = $request->getParam($field);
try {
$rule->setName(ucfirst($field))->assert($request->getParam($field));
} catch (NestedValidationException $e) {
$this->errors[$field] = $e->getMessages()[0];
}
}
if ($this->failed()) {
$_SESSION['validation'] = [
'errors' => $this->errors,
'values' => $this->values,
];
}
return $this;
}
public function failed()
{
return !empty($this->errors);
}
}
Using Respect\Validation. Also, is this the right use of Middlewares?
Thanks in advance.
try creating a separate file for the methods, and calling it from the middleware:
<?php
class AuthMiddleware extends Middleware {
public function __invoke($request, $response, $next) {
if (!$this->container->auth->check()) {
$this->container->flash->addMessage('danger', 'Please sign in to continue.');
return $response->withRedirect($this->container->router->pathFor('auth.signin'));
}
$response = $next($request, $response);
return $response;
}
}
while the Auth class would have those methods to check:
<?php
public function check () {
return isset($_SESSION['user']);
}
public function user() {
if (isset($_SESSION['user'])) {
return User::find($_SESSION['user'])->first();
} else {
return false;
}
}
Don't forget to include the Auth Class within your $app:
<?php
$container['auth'] = function ($container) {
return new \App\Auth\Auth();
};