My finder from Auth has conditions that I need to access $this->request but I don't have access for that on UsersTable.
AppController::initialize
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth',
]
]
]);
UsersTable
public function findAuth(Query $query, array $options)
{
$query
->select([
'Users.id',
'Users.name',
'Users.username',
'Users.password',
])
->where(['Users.is_active' => true]); // If I had access to extra data passed I would use here.
return $query;
}
I need pass extra data from AppController to finder auth since I don't have access to $this->request->data on UsersTable.
Update
People are saying on comments that is a bad design so I will explain exactly what I need.
I have a table users but each user belongs to a gym.
The username(email) is unique only to a particular gym so I can have a example#domain.comfrom gym_id 1 and another example#domain.com from gym_id 2.
On login page I have the gym_slug to tell to auth finder which gym the user username that I provided belongs.
To my knowledge, there is no way to do this by passing it into the configuration in 3.1. This might be a good idea submit on the cakephp git hub as a feature request.
There are ways to do it by creating a new authentication object that extends base authenticate and then override _findUser and _query. Something like this:
class GymFormAuthenticate extends BaseAuthenticate
{
/**
* Checks the fields to ensure they are supplied.
*
* #param \Cake\Network\Request $request The request that contains login information.
* #param array $fields The fields to be checked.
* #return bool False if the fields have not been supplied. True if they exist.
*/
protected function _checkFields(Request $request, array $fields)
{
foreach ([$fields['username'], $fields['password'], $fields['gym']] as $field) {
$value = $request->data($field);
if (empty($value) || !is_string($value)) {
return false;
}
}
return true;
}
/**
* Authenticates the identity contained in a request. Will use the `config.userModel`, and `config.fields`
* to find POST data that is used to find a matching record in the `config.userModel`. Will return false if
* there is no post data, either username or password is missing, or if the scope conditions have not been met.
*
* #param \Cake\Network\Request $request The request that contains login information.
* #param \Cake\Network\Response $response Unused response object.
* #return mixed False on login failure. An array of User data on success.
*/
public function authenticate(Request $request, Response $response)
{
$fields = $this->_config['fields'];
if (!$this->_checkFields($request, $fields)) {
return false;
}
return $this->_findUser(
$request->data[$fields['username']],
$request->data[$fields['password']],
$request->data[$fields['gym']],
);
}
/**
* Find a user record using the username,password,gym provided.
*
* Input passwords will be hashed even when a user doesn't exist. This
* helps mitigate timing attacks that are attempting to find valid usernames.
*
* #param string $username The username/identifier.
* #param string|null $password The password, if not provided password checking is skipped
* and result of find is returned.
* #return bool|array Either false on failure, or an array of user data.
*/
protected function _findUser($username, $password = null, $gym = null)
{
$result = $this->_query($username, $gym)->first();
if (empty($result)) {
return false;
}
if ($password !== null) {
$hasher = $this->passwordHasher();
$hashedPassword = $result->get($this->_config['fields']['password']);
if (!$hasher->check($password, $hashedPassword)) {
return false;
}
$this->_needsPasswordRehash = $hasher->needsRehash($hashedPassword);
$result->unsetProperty($this->_config['fields']['password']);
}
return $result->toArray();
}
/**
* Get query object for fetching user from database.
*
* #param string $username The username/identifier.
* #return \Cake\ORM\Query
*/
protected function _query($username, $gym)
{
$config = $this->_config;
$table = TableRegistryget($config['userModel']);
$options = [
'conditions' => [$table->aliasField($config['fields']['username']) => $username, 'gym' => $gym]
];
if (!empty($config['scope'])) {
$options['conditions'] = array_merge($options['conditions'], $config['scope']);
}
if (!empty($config['contain'])) {
$options['contain'] = $config['contain'];
}
$query = $table->find($config['finder'], $options);
return $query;
}
}
For more information see this: Creating Custom Authentication Objects
I know this is an old question but I thought I would post the finder I am using in one of our SaaS apps built on Cakephp 3. Does it follow DRY etc probably not. To say everything can be done X or Y way ..... you always have to bend the rules. In this case depending on the URL (xdomain.com or ydomain.com) our app figures out who the customer is and changes layouts etc. Also the user based is tied to Email & site_id much like yours
public function findAuth(\Cake\ORM\Query $query, array $options) {
$query
->select([
'Users.id',
'Users.email',
'Users.password',
'Users.site_id',
'Users.firstname',
'Users.lastname'])
->where([
'Users.active' => 1,
'Users.site_id'=> \Cake\Core\Configure::read('site_id')
]);
return $query;
}
Anyway hope it helps someone
Related
Click here
to see my diagram app.
Hey, my client change her DNS (like that : xxx-dns.com to dns.com) and this is ok, except one problem.. In react native app, I have contact form for send message to pro "partner", and this pro partner are in the process of switching their emails from xxx-dns.com to dns.com.
BUT the contact form provide by SwitchMailer doesn't work with the new DNS, I have check all configuration files, same for the server, the controller is (I think) ok.
Please help me, it's been a week
I have var_dump() all information in my controller, check api endpoint, navigate into my server and check configuration files.
This is my contact controller :
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Twig\Environment as Twig;
class ContactController extends AbstractController
{
/**
* #var string
*/
private $mailFrom;
/**
* #var string
*/
private $backUrl;
private $tokenStorage;
public function __construct(string $mailFrom, string $backUrl, TokenStorageInterface $tokenStorage)
{
$this->mailFrom = $mailFrom;
$this->backUrl = $backUrl;
$this->tokenStorage = $tokenStorage;
}
/**
* #Route("/contact", name="app_send_contact")
*/
public function sendAction(\Swift_Mailer $mailer, Twig $twig, Request $request)
{
// Retrieve user
$currentUser = $this->tokenStorage->getToken()->getUser();
if (!$currentUser) {
return $this->json(array('success' => false, 'message' => 'Utilisateur introuvable.'));
}
// Prepare parameters
$contactObject = $request->request->get('object');
$contactMessage = $request->request->get('message');
// Retrieve establishment
$establishment = $currentUser->getEstablishments()->first();
if (!$establishment) {
return $this->json(array('success' => false, 'message' => 'Etablissement introuvable.'));
}
// Prepare mail
$mailTitle = $currentUser->getFirstname() . ' ' . $currentUser->getLastname() . ' vous a envoyé un message : ' . $contactObject;
// Send mail to establishment
$message = (new \Swift_Message($mailTitle))
->setFrom($this->mailFrom)
->setTo($establishment->getEmail())
->setReplyTo($currentUser->getEmail(), $currentUser->getFirstname() . ' ' . $currentUser->getLastname())
->setBody(
$twig->render(
'emails/contact.html.twig',
array(
'user' => $currentUser,
'establishment' => $establishment,
'contact_object' => $contactObject,
'contact_message' => $contactMessage,
'back_url' => $this->backUrl,
)
),
'text/html'
);
$mailer->send($message);
// Prepare mail
$mailTitle = 'Confirmation de demande de contact';
// Send mail to parent
$message = (new \Swift_Message($mailTitle))
->setFrom($this->mailFrom)
->setTo($currentUser->getEmail())
->setBody(
$twig->render(
'emails/contact_confirmation.html.twig',
array(
'user' => $currentUser,
'establishment' => $establishment,
'contact_object' => $contactObject,
'contact_message' => $contactMessage,
'back_url' => $this->backUrl,
)
),
'text/html'
);
$mailer->send($message);
return $this->json(array('success' => true));
}
}```
On login page I get this in dev.log:
**Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []**
Plus on the register page, after submitting I get
**The CSRF token is invalid. Please try to resubmit the form.**
And in dev.log:
**Guard authentication failed. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\InvalidCsrfTokenException(code: 0):**
The same code is working on my colleague's machine cannot get where can be the problem.
security.yaml
firewalls:
static_pages:
pattern: ^/static
security: false
oauth_token:
pattern: ^/oauth/token
security: false
oauth_authorize:
pattern: ^/oauth/auth
security: false
# Add your favorite authentication process here
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
user_checker: security.user_checker
form_login:
provider: app_user
csrf_token_generator: security.csrf.token_manager
default_target_path: app_homepage
logout: true
anonymous: true
guard:
provider: app_user
entry_point: App\Security\LoginFormAuthenticator
authenticators:
- App\Security\GoogleAuthenticator
- App\Security\FacebookAuthenticator
- App\Security\LoginFormAuthenticator
SecurityController.php
<?php
namespace App\Controller\Identity;
use App\Controller\BaseController;
use App\Entity\User;
use App\Security\AuthenticationManager;
use App\Util\UserManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
/**
* Class SecurityController
*/
class SecurityController extends BaseController
{
/**
* #Route("/login", name="app_login")
*
* #param AuthenticationUtils $authenticationUtils
*
* #return Response
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('Security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error
]);
}
/**
* #Route("/admin-login", name="app_security_admin_login", methods={"POST", "GET"})
*
* #param Request $request
*
* #return Response
*/
public function adminLogin(Request $request)
{
/** #var $session Session */
$session = $request->getSession();
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} elseif ($request->query->has('error')) {
$error = $request->query->get('error');
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = (is_null($session)) ? '' : $session->get($lastUsernameKey);
$tokenManager = $this->getToken();
$csrfToken = $tokenManager
? $tokenManager->getToken('authenticate')->getValue()
: null;
return $this->render('Security/admin_login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
]);
}
/**
* #Route("/admin-login-check", name="app_security_admin_login_check", methods={"POST", "GET"})
*
* #param Request $request
* #param UserManager $userManager
*
* #return Response
*/
public function adminLoginCheck(Request $request, UserManager $userManager)
{
$username = $request->request->get('_username');
$password = $request->request->get('_password');
$encoders = new NativePasswordEncoder(13);
$factory = new EncoderFactory([$encoders]);
/** #var User $user */
$user = $userManager->findUserByUsername($username);
if(!$user){
return $this->redirectToRoute('app_security_admin_login',
['error' => 'Username doesnt exists']
);
}
$encoder = $factory->getEncoder(User::class);
$salt = $user->getSalt();
if(!$encoder->isPasswordValid($user->getPassword(), $password, $salt)) {
return $this->render("Security/admin_login.html.twig", [
'error' => 'Username or Password not valid'
]);
}
$token = $this->get(AuthenticationManager::class)->authenticate($user);
return $this->redirectToRoute("admin_homepage");
}
/**
* #Route("/login", name="app_security_login", methods={"POST", "GET"})
*
* #param Request $request
*
* #return Response
*/
public function loginAction(Request $request)
{
/** #var $session Session */
$session = $request->getSession();
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
/** get the error if any (works with forward and redirect -- see below) */
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
/** last username entered by the user */
$lastUsername = (is_null($session)) ? '' : $session->get($lastUsernameKey);
$tokenManager = $this->getToken();
$csrfToken = $tokenManager
? $tokenManager->getToken('authenticate')->getValue()
: null;
return $this->render('Security/login.html.twig',[
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
]);
}
/**
* #Route("/login_check", name="app_security_check", methods={"POST"})
*/
public function checkAction()
{
throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
}
/**
* #Route("/logout", name="app_security_logout", methods={"GET"})
*/
public function logoutAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
}
protected function getToken()
{
return $this->get("security.csrf.token_manager");
}
}
I could provide other files if needed.
Thank you.
The solution was to set the path for the session even though the log complained about other things:
# config/packages/framework.yaml
session:
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
Iam using oriceon/oauth-5-laravel for connecting with instagram.I authenticated and i got my name and id by through this request
$result = json_decode($instagramService->request('users/self'), true);
Now i want to follow a person by using this oriceon/oauth-5-laravel.
How i could post the request to follow a person by using this .
**NOTE:**dont refer the instagram developer page.I reffred it a lot.But i couldnt put a post request for ORICEON/laravel oauth wrapper for instagram.
HELP ME.TYSM in advance
try this .Copy this method to your controller and do proper routing.
public function followWithInstagram(Request $request)
{
$instagram_id='id of the user that you want to follow';
$code = $request->get('code');
$instagramService = \OAuth::consumer('Instagram');
if ( ! is_null($code))
{
$state = isset($_GET['state']) ? $_GET['state'] : null;
$instagramService->requestAccessToken($code, $state);
$linkData = [
'action' =>'follow',
];
$result =json_decode($instagramService->request('https://api.instagram.com/v1/users/'.$instagram_id.'/relationship','POST',$linkData),true);
if(!$result){
return ("Failed");
}
else {
return ("SUCCESS");
}
} else {
$url = $instagramService->getAuthorizationUri();
return redirect((string)$url);
}
}
I have a Module called "Backend" and in this module I want to check for valid authentication on all pages except the backend_login page. How do I do this? I tried to add it to the onBootstrap in the Backend/Module.php , but it turns out that is called in my other modules as well... which is of course not what I want.
So how do I do this?
Thanks in advance!
To get clear information about zf2 authentication you can follow:
ZF2 authentication
adapter auth
database table auth
LDAP auth
digest auth....These all are different methods here is an example of database table auth:
in every controller's action, where you need user auth something should like this:
use Zend\Authentication\Result;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Db\Adapter\Adapter as DbAdapter;
use Zend\Authentication\Adapter\DbTable as AuthAdapter;
public function login($credential)
{
$bcrypt = new Bcrypt();
$user = new User();
$auth = new AuthenticationService();
$user->exchangeArray($credential);
$password = $user->password;
$data = $this->getUserTable()->selectUser($user->username);
if (!$data){
$message = 'Username or password is not correct!';
}
elseif($auth->getIdentity() == $user->username){
$message = 'You have already logged in';
}
elseif($bcrypt->verify($password, $data->password)){
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$authAdapter = new AuthAdapter(
$dbAdapter,
'user',
'username',
'password'
);
$authAdapter -> setIdentity($user->username) -> setCredential($data->password);
$result = $auth->authenticate($authAdapter);
$message = "Login succesfull.Welcome ".$result->getIdentity();
} else {
$message = 'Username or password is not correct';
}
return new ViewModel(array("message" =>$message));
}
Like this in every action you can check whether it is authenticated or not
if($auth -> hasIdentity()){
//your stuff
}
else{
//redirected to your login route;
}
I had once a similar problem and figured it out within my Module.php in the onBootstrap() function. Try this, it worked for me:
class Module {
// white list to access with being non-authenticated
//the list may contain action names, controller names as well as route names
protected $whitelist = array('login');
//....
public function onBootstrap($e){
$app = $e->getApplication();
$em = $app->getEventManager();
$sm = $app->getServiceManager();
$list = $this->whitelist;
$auth = new AuthenticationService();
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($list, $auth) {
$match = $e->getRouteMatch();
// No route match, this is a 404
if (!$match instanceof RouteMatch) {
return;
}
// Route is whitelisted
$action = $match->getParam('action');
if (in_array($action, $list) ) {
return;
}
// User is authenticated
if ($auth->hasIdentity()){
return;
}
// the user isn't authenticated
// redirect to the user login page, as an example
$router = $e->getRouter();
$url = $router->assemble(array(
'controller' => 'auth',
'action'=>'login'
), array(
'name' => 'route_name',
));
$response = $e->getResponse();
$response->getHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302);
return $response;
}, -100);
}
}
Or you may see bjyauthorize.
I'm trying to login with the following controller action, but my login attempt keeps failing (I get the 'invalid username and/or password' message). What am I doing wrong? I also tried the other method given in the examples in the auth documentation, Auth::instance()->login($user->username, $form->password);, but I get the same result. Kohana version is 2.3.4.
public function login() {
$auth = Auth::instance();
if ($auth->logged_in()) {
url::redirect('/account/summary');
}
$view = new View('login');
$view->username = '';
$view->password = '';
$post = $this->input->post();
$form = new Validation($post);
$form->pre_filter('trim', 'username')
->pre_filter('trim', 'password')
->add_rules('username', 'required');
$failed = false;
if (!empty($post) && $form->validate()) {
$login = array(
'username' => $form->username,
'password' => $form->password,
);
if (ORM::factory('user')->login($login)) {
url::redirect('/accounts/summary');
} else {
$view->username = $form->username;
$view->message = in_array('required', $form->errors()) ?
'Username and password are required.' :
'Invalid username and/or password.';
}
}
$view->render(true);
}
Figured out my problem... Something in my registration process is missing, because it's creating the user record but not the role-to-user assoc record. Login needs a specific role to log in to, or it won't work even with a valid username and password. Manually inserting the record allowed my to log in, so I'll just have to debug my registration action a bit.