How do I avoid redirecting 404's to the login form in a Zend Framework 1 controller plugin? - zend-framework

I made a controller plugin to handle authentication. If a user tries to access a page without being logged in, it saves the route of the page he was trying to access, forwards to the login page, and then when the user logs in, it redirects him to where he was trying to go.
But if the user tries to access a nonexistent page while logged out, then it still forwards to the sign-in form, but when the user signs in, it brings up an error.
How do I bring up a 404 error before the user signs in? I think I need to detect whether the route is valid within dispatchLoopStartup(). How do I do that? Or is there some other way of doing this?
class Chronos_Controller_Plugin_Auth extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$request->setParam('userName', $auth->getIdentity());
} else {
$request->setParam('origModule', $request->getModuleName())
->setParam('origController', $request->getControllerName())
->setParam('origAction', $request->getActionName())
->setModuleName('default')
->setControllerName('sign')
->setActionName('in');
}
}
}

Try something like this:
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$request->setParam('userName', $auth->getIdentity());
} else if ($dispatcher->isDispatchable($request)) {
$request->setParam('origModule', $request->getModuleName())
->setParam('origController', $request->getControllerName())
->setParam('origAction', $request->getActionName())
->setModuleName('default')
->setControllerName('sign')
->setActionName('in');
}
}

Related

Form redirect for confirmation

Form redirect for confirmation can be currently managed using one of these two options:
1/ Flash message: using flashbag on the form page or another page like this:
$this->addFlash('success', 'Thank you');
return $this->redirectToRoute('confirmation_page');
2/ Confirmation page: using a dedicated confirmation like this:
return $this->redirectToRoute('confirmation_page');
BUT using option 2 makes the confirmation_page directly accessible from the browser without having submitted the form before. I am currently using flashbag mechanism to fix it by adding a $this->addFlash('success', true); before the redirection in the form and then checking the flashbag content in the confirmation page so that the route is accessible only once after being successfully redirected from the form.
Is there any best practice or more appropriate way to manage it?
/**
* #Route("/confirmation", methods="GET", name="confirmation_page")
*/
public function confirmation(): Response
{
$flashbag = $this->get('session')->getFlashBag();
$success = $flashbag->get("success");
if (!$success) {
return $this->redirectToRoute('app_home');
}
return $this->render('templates/confirmation.html.twig');
}
Flash Message is designed to display messages. Instead, use sessions in your application.
When submitting the confirmation form, create a variable in the session before the redirect
$this->requestStack->getSession()->set('verifyed',true);
return $this->redirectToRoute('confirmation_page');
Use the created variable in your method
public function confirmation(): Response
{
if (!$this->requestStack->getSession()->get('verifyed')) {
return $this->redirectToRoute('app_home');
}
return $this->render('templates/confirmation.html.twig');
}
Don't forget to inject the RequestStack into your controller
private RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}

Laravel persist $request->flash old() session

So, I've build in functionality which redirects the logged in user to a pincode screen after 10 minutes of inactivity, when the user enters the correct pincode he or she is redirected to the page where he or she was navigating to.. So far so good but...
Imagine the user is filling out a form and waits ten minutes before hitting the submit button and is then redirected to the pincode page, after inputting the correct pincode he is on the form again but all data on it is gone.
What I want is to remember all the filled data, I've tried it via the old() method but that only persists for one request.
PincodeCheck Middleware
if($difference_in_minutes > config('pincode.lifetime')) {
return redirect()->route('pincode')->withInput();
}
PincodeController
public function index(Request $request)
{
// $request->old() is holding the values
return view('pincode.index');
}
public function unlock(Request $request)
{
// This is the after submit function, $request->old() is empty here
if(Hash::check($request->pincode, auth()->user()->pincode) == true) {
$request->session()->put('pincode-timestamp', date('U'));
$path = config('app.homeroute');
if($request->session()->has('intended.get.path')) {
$path = $request->session()->get('intended.get.path');
}
return redirect($path);
}
return redirect()->route('pincode');
}

Laravel Socialite with Facebook not logging in

I'm following the documentation exactly.
https://github.com/laravel/socialite and https://laravel.com/docs/5.1/authentication#social-authentication
I've created my app on Facebook and got everything working. When I click my log in with Facebook button, it authorizes the app and takes me back to my site.
However, it doesn't show me as logged in. If I dd() instead of the redirect below, I get all of the data from my Facebook account. But the pages that are only visible to logged in users, aren't visible.
Here is my controller:
public function redirectToProvider()
{
return Socialite::driver('facebook')->redirect();
}
public function handleProviderCallback()
{
$user = Socialite::driver('facebook')->user();
return redirect('my-profile')
->with('message', 'You have signed in with Facebook.');
}
Here are my routes:
Route::get('login/facebook', 'Auth\AuthController#redirectToProvider');
Route::get('login/facebook/callback', 'Auth\AuthController#handleProviderCallback');
Socialite is installed properly in composer.json. The classes are in config/app.php and the IDs for my FB app are in config/services.php.
Any ideas as to why it's not working?
In the handleProviderCallback method you need to create and authenticate the user returned by the driver.
Create the user if doesn't exist:
$userModel = User::firstOrNew(['email' => $user->getEmail()]);
if (!$userModel->id) {
$userModel->fill([.....]);
$userModel->save();
}
Then authenticate the user:
Auth::login($userModel);
Your method will look like this:
public function handleProviderCallback() {
$user = Socialite::driver('facebook')->user();
$userModel = User::firstOrNew(['email' => $user->getEmail()]);
if (!$userModel->id) {
$userModel->fill([.....]);//Fill the user model with your data
$userModel->save();
}
Auth::login($userModel);
return redirect('my-profile')
->with('message', 'You have signed in with Facebook.');
}

Exchanging Facebook Auth Code for Access Token using the PHP SDK

I am trying to build a server-to-server auth flow using the Facebook PHP SDK and no Javascript, as outlined here. So far, I have successfully created a LoginUrl that lets the User sign in with Facebook, then redirect back to my App and check the state parameter for CSFR protection.
My Problem is, that I can't seem to get the API-call working that should swap my Auth Code for an access token. I pillaged every similar problem anyone else that Google was able to find had encountered for possible solutions.
Yet the end result was always the same: no access token, no error message that I could evaluate.
Researching the topic yielded the following advice, which I tested:
The URL specified in the App Settings must be a parent folder of $appUrl.
use curl to make the request instead of the SDK function api()
I've been at this for 2 days straight now and really could use some help.
<?php
require '../inc/php-sdk/src/facebook.php';
// Setting some config vars
$appId = 'MY_APP_ID';
$secret = 'MY_APP_SECRET';
$appUrl = 'https://MY_DOMAIN/appFolder';
$fbconfig = array('appId'=>$appId, 'secret'=>$secret);
$facebook = new Facebook($fbconfig);
// Log User in with Facebook and come back with Auth Code if not yet done
if(!(isset($_SESSION['login']))){
$_SESSION['login']=1;
header('Location: '.$facebook->getLoginUrl());
}
// process Callback from Facebook User Login
if($_SESSION['login']===1) {
/* CSFR Protection: getLoginUrl() generates a state string and stores it
in "$_SESSION['fb_'.$fbconfig['appId'].'_state']". This checks if it matches the state
obtained via $_GET['state']*/
if (isset($_SESSION['fb_'.$fbconfig['appId'].'_state'])&&isset($_GET['state'])){
// Good Case
if ($_SESSION['fb_'.$fbconfig['appId'].'_state']===$_GET['state']) {
$_SESSION['login']=2;
}
else {
unset($_SESSION['login']);
echo 'You may be a victim of CSFR Attacks. Try logging in again.';
}
}
}
// State check O.K., swap Code for Token now
if($_SESSION['login']===2) {
$path = '/oauth/access_token';
$api_params = array (
'client_id'=>$appId,
'redirect_uri'=>$appUrl,
'client_secret'=>$secret,
'code'=>$_GET['code']
);
$access_token = $facebook->api($path, 'GET', $api_params);
var_dump($access_token);
}
The easiest way I found to do this is to extend the Facebook class and expose the protected getAccessTokenFromCode() method:
<?php
class MyFacebook extends Facebook {
/** If you simply want to get the token, use this method */
public function getAccessTokenFromCode($code, $redirectUri = null)
{
return parent::getAccessTokenFromCode($code, $redirectUri);
}
/** If you would like to get and set (and extend), use this method instead */
public function setAccessTokenFromCode($code)
{
$token = parent::getAccessTokenFromCode($code);
if (empty($token)) {
return false;
}
$this->setAccessToken($token);
if (!$this->setExtendedAccessToken()) {
return false;
}
return $this->getAccessToken();
}
}
I also included a variation on the convenience method I use to set the access token, since I don't actually need a public "get" method in my own code.

Using Zend Auth with external authentication mechanism

I have a Drupal site and a Zend application. The main thing is the Drupal site, where the users are stored & everything.
I want my users to be automatically logged in to the Zend app when they log in on Drupal. The problem is that Drupal changes the session cookie to SESS* where * is some random (EDIT: not random, but based on protocol and domain) string.
Is there any way I can tell Zend to use this cookie as a session identifier and to log the user automatically?
You have to write your own authentication adapter:
class YourApp_Auth_Adapter_DrupalBridge implements Zend_Auth_Adapter_Interface
{
/**
* #return Zend_Auth_Result
*/
public function authenticate()
{
// Check if the Drupal session is set by reading the cookie.
// ...
// Read the current user's login into $username.
// ...
// Create the authentication result object.
// Failure
if (null === $username) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, null);
}
// Success
return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $username);
}
}
Then process your authentication:
$adapter = new YourApp_Auth_Adapter_DrupalBridge();
$result = Zend_Auth::getInstance()->authenticate($adapter);
if ($result->isValid()) {
// User is logged in
}