zend auth conflicts with joomla's session - php

i m trying to integrate an application build in zend into a website that is build in joomla.
when i try to set zend's auth it gives me an error "session has already been started by session.auto-start or session_start()" since i'm getting logged in user's details using joomla's functions
is it possible to have zend's auth in parallel with joomla's session?
if not then is it possible to get logged in user's information without using any of joomla's functions(i think this is not possible)
how do i use joomla's already started session into my zend application?

Could be a problem with the filename of the Session cookie. If you need seperated Session try to set the Session options:
Zend_Session::setOptions(array(
'name' => 'ZENDSession',
'save_path' => $appRoot . '/session',
'remember_me_seconds' => 864000
));
try
{
Zend_Session::start();
}
catch (Exception $e)
{
//Exception
}
If you want to Authenticate against the Joomla UserDatabase, try this Zend_Auth_Adapter:
http://www.plainlycode.com/Joomla/Joomla-Authentication-Adapter-for-the-Zend-Framework.html

Related

SimpleSMLphp Custom Module Authentication Triggering Twice

Background: I am trying to set up single sign on (SSO) for users such that they can authenticate to my website and not have to authenticate a second time to our third-party MSP's website. Ideally, the user clicks a link on our website and is taken to the third-party site already logged in and landing on the dashboard (if the account doesn't exist, it is created during this step). We are not using SAML for authentication as a security feature, so all that we need the SAML code for is just producing cookies that prevent the user from having to log in again when he/she gets to our vendor's site. This third party MSP does not support authentication via API or web service and therefore I have been tasked with implementing SAML, their only supported SSO method. I am new to SAML (but not PHP or development) and have been learning as I go. I am told it will support the goals described above.
I initially tried using LDAP as the authentication source as this is what I use for authentication to my website, but this resulted in me getting directed to a login page with no discernible way to instead just pass parameters to SimpleSAMLphp to tell it "the user is already authenticated, all I need you to do is give me valid cookies so I can get past the third party website's authentication checks".
So I switched to writing a custom authentication module. I opened up the GitHub for SimpleSAMLphp and used the "UserPassBase" class as an example to create my own authentication module that inherits from the "Source" class. Because I don't need to re-authenticate the user against LDAP a second time since they're already logged in to our website, I created a simple "authenticate" function that just sets the $state['Attributes'] array.
Here is the code for my custom module:
<?php
namespace SimpleSAML\Module\productauth\Auth\Source;
use SimpleSAML\Auth;
/**
Author: Joey
Class developed to be used as a custom authentication module for simpleSAMLphp. This class will take an existing session from a product website and use it to create a SAML session and redirect to a website.
**/
class ProductAuth extends \SimpleSAML\Auth\Source {
const STAGEID = '\SimpleSAML\Module\productauth\Auth\ProductAuth.state';
const AUTHID = '\SimpleSAML\Module\productauth\Auth\ProductAuth.AuthId';
private $user;
public function __construct($info, $config) { // parameters aren't used, just filler from base class
$info = array("AuthId" => "productauth");
parent::__construct($info, $config);
}
public function login($user, $redirectURL) {
$this->user = $user; // normally I'd set this in the constructor, but the overload has my hands tied as far as function definitions go
$this->initLogin($redirectURL); // calls authenticate function and then, if no exceptions, parent::loginCompleted which redirects to the given URL
}
public function authenticate(&$state) { // called by parent::initLogin
$state[self::AUTHID] = $this->authId;
$state['Attributes'] = [
'uid' => [$this->user->uid],
'givenName' => [$this->user->givenName],
'sn' => [$this->user->sn],
'mail' => [$this->user->mail]
];
$id = Auth\State::saveState($state, self::STAGEID);
}
}
?>
I am calling it from a controller class on my website:
private function goToTrainingSite() {
require_once("../third-party-libs/simplesamlphp/_include.php");
global $TRAINING_URL;
$user = $_SESSION['subject']->user;
$samlObj = new SimpleSAML\Module\productauth\Auth\Source\ProductAuth(array(), array());
$samlObj->login($user, $TRAINING_URL);
}
I mimicked the flow of the "UserPassBase" class (https://github.com/simplesamlphp/simplesamlphp/blob/master/modules/core/lib/Auth/UserPassBase.php), but it seems that despite all of my authentication working and setting a SimpleSAMLAuth cookie, when the parent::loginCompleted function in the "Source" class (https://github.com/simplesamlphp/simplesamlphp/blob/master/lib/SimpleSAML/Auth/Source.php) runs, it redirected me to the third party site. I then see the following in the logs:
SAML2.0 - IdP.SSOService: incoming authentication request: [REDACTED DATA]
Session: 'productauth' not valid because we are not authenticated.
I have been trying for 3 days to figure out why it seems as though despite setting SimpleSAML session cookies with a completed, successful authentication, that upon receiving the auth request from the SP, my SimpleSAMLphp code just pretends to not know about the completed auth and tries to authenticate again... but because it is not being called from my code, it doesn't have access to the $user variable which contains all of the attributes I need to place on the user when he/she authenticates to this third party website. It seems that when it receives an authentication request, my SimpleSAMLphp installation starts a new session and tries a brand new authentication.
I have delved into a lot of the code of SimpleSAMLphp and tried to understand what is going on, but it seems that there is just no reasonable way to authenticate by calling an authentication source from PHP code and being able to skip the SP-initiated authentication. I have tried:
Using the SimpleSAML API (https://simplesamlphp.org/docs/stable/simplesamlphp-sp-api) to call my authentication source, but there seems to be no way to pass that $user variable I need the attributes from.
Trying to load the cookies in the "Session" class when it is checking for valid sessions... but it seems like the cookies from the successful auth session initiated by my code are just gone and nowhere to be found.
I decided to stop focusing on trying to get the $user variable and the data I needed to the second authentication, and instead focus on WHY the second authentication was even happening. I looked at the cookies and thought about how the data was being retrieved, and made a correct hunch that our application's custom session handler might be at fault for SimpleSAMLphp's inability to recognize the first authentication. Our custom session handler stores our sessions in the database, but SimpleSAMLphp expects to use the default PHP session handler to manage its session. Therefore, my first authentication was being sent to the database and when SimpleSAMLphp started looking for it where PHP sessions are usually stored, it didn't see it and assumed it needed to kick off another authentication session from scratch.
Using SimpleSAMLphp's documentation for service providers and a lot of my own debugging, I changed the function in my controller like so:
private function goToTrainingSite() {
require_once ("../third-party-libs/simplesamlphp/_include.php");
global $TRAINING_URL;
$joeySiteSession = $_SESSION;
$user = $_SESSION ['subject']->user; // save user to variable before the Joey's Site session is closed
session_write_close (); // close Joey's Site session to allow SimpleSAMLphp session to open
session_set_save_handler ( new SessionHandler (), true ); // stop using SessionHandlerJoey and use default PHP handler for SimpleSAMLphp
$samlObj = new SimpleSAML\Module\joeysiteauth\Auth\Source\JoeySiteAuth ( array (), array () );
$samlObj->login ( $user, function () { return;} ); // use custom authentication module to set atttributes and everything SimpleSAMLphp needs in the auth session/cookie
$session = \SimpleSAML\Session::getSessionFromRequest ();
$session->cleanup (); // must call this function when we are done with SimpleSAMLphp session and intend to use our Joey's Site session again
session_write_close ();
$_SESSION = $joeySiteSession; // restore Joey's Site session
header ( "Location: {$TRAINING_URL}" );
}

Drupal 8 external/custom authentication provider

I am experimenting with Drupal 8 as our customer websites. Our customers authenticate through our own authentication application at the moment which speaks to our document store (instead of MySQL) to authenticate a user and provide them with a unique session ID (JWT eventually but that's another day and conversation) which we can use to then query a REST API and get user data in any of our other self apps.
We are moving over from an old JSP based websites to drupal as our apps are now written in Symfony 3 but want our customer websites to be Drupal 8.
Here's the bit I am trying to work out. If I authenticate in our old website I want to be able to redirect to the Drupal 8 website with the session ID we have in our hand and use that to fetch a object back of our logged in user. I have this bit working fine but I now am in a position to say... Ok I have the user object back, the 3rd party service has said that session ID is valid so we know we are authenticated.
Please refer to the below flow chart. I want to be able to also authenticate in Drupal 8 manually. Is this possible (I am sure it is) and if so can someone point me in the right direction as to what I need/should be doing, API's I should be calling?
Thank you kindly and good day :)
You should use the External Auth module.
A good exemple of how use this module is the SimpleSamlPHP Auth
Ok so it turned out not to be that tricky in the end. I thought I would have to extend and implement various class and create my own provider (which is probably the best practice) but for KISS sake I found another way.
Create a user first if one does not exists based on the user data I get back from my external service. Then pass that created user to the user_login_finalize method (why are a lot of methods underscored Drupal...) which then authenticated my user.
public function inbound(Request $request)
{
// Point the guzzle client to our external session service.
$client = new GuzzleHttpClient([
'base_uri' => 'https://myexternalservice.com/apps/authentication/2/',
]);
// Attempt to send to request with the session ID from the parameters.
try {
$response = $client->request('GET', 'api/v1/user/' . $request->get('session_id'));
} catch (\Exception $e) {
throw new \HttpException($e->getMessage());
}
// Convert the response to an array.
$result = json_decode((string) $response->getBody(), true);
// Convert our array to a user entity.
if ($user = $this->convertResponseToUser($result['user'])) {
try {
// Attempt to load the user. If the user does not exist then create them first.
if (!$assumeUser = user_load_by_mail($user->getEmail())) {
// Create a Drupal user object.
$assumeUser = $this->createUser([
'name' => $user->getFirstName() . ' ' . $user->getLastName(),
'mail' => $user->getEmail()
]);
$assumeUser->save();
}
// Authenticate the user.
user_login_finalize($assumeUser);
} catch (\Exception $e) {
drupal_set_message(t('An unhandled exception occurred during authentication.'), 'error');
return $this->redirect('user.login');
}
}
return $this->redirect('mymodule.route');
}

Integrating SimpleSamlPHP with Zend

I'm pretty new to the Zend framework, and am attempting to integrate an SSO with an existing Zend site. I have installed SimpleSamlPHP and can connect to the ADFS server and get assertion data in the form of First Name and Last Name using the following script
$lib = "/var/simplesamlphp";
$sp = "wte-sp";
try {
if(!file_exists("{$lib}/_autoload.php")) {
throw(new Exception("simpleSAMLphp lib loader file does not exist: ".
"{$lib}/_autoload.php"));
}
include_once("{$lib}/_autoload.php");
$as = new SimpleSAML_Auth_Simple($sp);
$as->requireAuth();
$valid_saml_session = $as->isAuthenticated();
} catch (Exception $e) {
throw(new Exception("SSO authentication failed: ". $e->getMessage()));
return;
}
if (!$valid_saml_session) {
try {
$as = new SimpleSAML_Auth_Simple($sp);
$as->requireAuth();
} catch (Exception $e) {
throw(new Exception("SSO authentication failed: ". $e->getMessage()));
return;
}
}
$attributes = $as->getAttributes();
print_r($attributes);
I am now attempting to move this into a Zend controller, on page load I am redirected to the SSO page for authentication and returned to the correct page with no problems, however, it appears as if the $attributes array is empty. I have confirmed that I have got the SAML cookie set, and I am seeing SAML data when I var_dump($_SESSION); but it looks as if somewhere along the line Zend is doing something unknown to the data as I'm always hitting the if (!$valid_saml_session) { statement and getting stuck in an authentication redirect loop.
As mentioned before, the code works perfectly as a standalone page, but not being too clued up on Zend, I'm drawing a bit of a blank. I have changed the last lines to
$attributes = $as->getAttributes();
$this->view->attributes = $attributes;
And have sent the results to the view, but the array is empty.
I am able to get the Login and Logout URLs to the view with no problems.
EDIT - 21/12/15
Looks like this is related to the way Zend is setting up sessions. In my Bootstrap.php file I have
protected function _initSession()
{
Zend_Session::setOptions(array(
'save_path' => $this->options['resources']['session']['save_path'],
'use_only_cookies' => 'on',
'remember_me_seconds' => 86400
));
Zend_Session::start();
}
If I comment this out, I get the SSO information back with no problems. I'm concerned about making this change as I'm not sure of the implications across the application, does anyone have a better solution to stop Zend from trashing my SSO session data?

Get Magento Session from Zend framework (Another Application)

I want to get the Magento Session From Zend Framework (Another application). I need that the application in Zend Framework validates if the user in the admin is logged (Magento). I have the following code in my Zend Framework application :
// Initialize Magento
include_once 'C:/xampp/htdocs/mymagento/app/Mage.php';
// Initialize Magento
Mage::app('default');
// This initalizes the session, using 'adminhtml' as the session name.
// Just ignore the returned Mage_Core_Model_Session instance
Mage::getSingleton('core/session', array('name' => 'adminhtml'));
// Get a singleton instance of the Mage_Admin_Model_Session class
// This is just the 'admin' namespace of the current session. (adminhtml in this case)
$session = Mage::getSingleton('admin/session');
// Use the 'admin/session' object to check loggedIn status
if ( $session->isLoggedIn() ) {
echo "logged in";
} else {
echo "not logged in";
}
The response is allways "not logged in"; when i'm logged in Magento Backend.
The session is created for the parent domain only - the one Magento is launched from.
I assume that in your case Magento and "another zend app" are different domains, which may be triggering the issue. Both Magento and "another zend app" should be on one parent domains. E.g.:
(*.example.com | magento.example.com | anotherzendapp.example.com).
Another reason why this issue occurs is the autoload by another zend app. If this is the case, this solution should help:
$autoloaders = spl_autoload_functions();
$anotherZendApp = $autoloaders[0];
spl_autoload_unregister($anotherZendApp);
// YOU CODE
restore_error_handler();
restore_exception_handler();
spl_autoload_register($anotherZendApp);

Symfony1.4 visit session of other applications

I recently created a new symfony application on the existing website. Then, in this new application, I want to read the session of old applications(something like login user id). Unfortunately, in each application, the session are completely separate(I mean the symfony session, something like $this->getUser()->getAttribute("userSession")).
I guess the symfony session is implemented using $_SESSION like:
$_SESSION = array("symfonyapp1" => array(....), "symfonyapp2" => array(....));
So I wrote $_SESSION["test"] = "testStr" in the old application and wrote var_dump($_SESSION["test"]);. The screen simply prints "null", so my guess is wrong.
Then I think maybe I can read the configuration of a certain application and then get the user of that application. So I wrote the following code in my new application:
require_once($_SERVER['DOCUMENT_ROOT'].'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
$context = sfContext::createInstance($configuration);
var_dump($context->getUser()->getAttribute("userId"));
Unfortunately again, it prints "null".
I completely have no idea now. Any advice is greatly appreciated
Check session identifiers of your apps. Php's default session id is PHPSESSID and as far as I remember default session identifier for Symfony 1.4 apps is just symfony
You can change Symfony's session identifier by modyfing apps/YOUR_APP_NAME/config/factories.yml file, by setting:
all:
storage:
param:
session_name: PHPSESSID
By doing that your Symfony app will share the same session id as your old app and you will be able to read $_SESSION attributes in Symfony app

Categories