i have two methods:
method one catch the username and password
method 2 is a service that should catch the username and password through custom request headers
i want to add the username and password as custom headers to the current request so method 2 can handle them
method 2 has one parameter which is IRequest
i'm using OwnCloud, trying to make a plugin app to control Authentication, this works when calling OwnCloud through webdav in C# but i need it to control OwnCloud login page also by catching userName and password and use the same code in the module object
how can add new headers to the current request call since the header("..") function not working?
class Application extends App {
public function pre_login($parameters) { // method 1
$uid = $parameters['uid'];
$password = $parameters['password'];
header("UserId:" . $uid);
header("Password:" . $password);
}
}
class AuthModule implements IAuthModule {
public function auth(IRequest $request) { // method 2
$UserId = $request->getHeader('UserId'); // not working
$password= $request->getHeader('password'); // not working
}
}
If you have 2 different apps and want to get headers (on your api for example) take into account that some frameworks add additional info to headers. Try getHeader('HTTP_UserId')
Related
I'm trying to use the League OAuth2 Client to allow users to authenticate my Laravel web app to set appointments on their calendar. NOTE: I'm not trying to let users login to my site or authenticate into my site using OAuth! I just want to be able to let users add appointments to their own calendars.
I'm basically following the flow outlined here: https://github.com/thephpleague/oauth2-google and have created a single controller (called OauthController with a single method, redirectGoogle. My redirect route (which is registered with Google) is https://example.com/oauth2/google. When I hit this endpoint in my Laravel app, I get redirected to Google to approve my app to access my account data as expected, and then redirected back to the controller endpoint.
However it fails every time at the exit('Invalid state'); line.
Here's the controller method code:
public function redirectGoogle(Request $request)
{
$provider = new Google([
'clientId' => config('oauth.google_oauth_id'),
'clientSecret' => config('oauth.google_oauth_secret'),
'redirectUri' => 'https://example.com/oauth2/google',
]);
if (!empty($request->input('error'))) {
// Got an error, probably user denied access
dd($request->input('error'));
} elseif (empty($request->input('code'))) {
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
session(['oauth2state', $provider->getState()]);
Log::info('Storing provider state ' . session('oauth2state')); <-- Log entry exists so we know session value was written
header('Location: ' . $authUrl);
exit;
} elseif (empty($request->input('state')) || ($request->input('state') !== session('oauth2state', false))) {
Log::error($request->input('state') . ' did not equal stored value ' . session('oauth2state', false)); <-- Log entry exists
// State is invalid, possible CSRF attack in progress
exit('Invalid state'); <-- Breaks here
} else {
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $request->input('code')
]);
// Optional: Now you have a token you can look up a users profile data
try {
// We got an access token, let's now get the owner details
$ownerDetails = $provider->getResourceOwner($token);
// Use these details to create a new profile
dd('Hello %s!', $ownerDetails->getFirstName());
} catch (Exception $e) {
// Failed to get user details
dd('Something went wrong: ' . $e->getMessage());
}
// Use this to interact with an API on the users behalf
echo $token->getToken() . PHP_EOL;
// Use this to get a new access token if the old one expires
echo $token->getRefreshToken() . PHP_EOL;
// Unix timestamp at which the access token expires
echo $token->getExpires() . PHP_EOL;
dd();
}
}
The strange thing is that the log messages noted in the code above both exist, and the values match (at least, it is attempting to write the first session variable with a value that would match the second log file's value):
[2020-05-04 21:02:48] local.INFO: Storing provider state 4963a33bbd5bcf52d3e21c787f24bd7b
[2020-05-04 21:02:51] local.ERROR: 4963a33bbd5bcf52d3e21c787f24bd7b did not equal stored value <null>
Why is it that the second time through the code the oauth2state session value is null, when it was successfully written on the first loop?
NOTE: the problem appears to be that the sessions are different, which makes sense, but how can this session stay consistent, or otherwise keep the data straight?
[2020-05-05 15:25:06] local.INFO: Session id: bV7F5mNM69rJAVJNWK9ZD0rcoN284FxXvjNAmUiw
[2020-05-05 15:25:06] local.INFO: Storing provider state 7351b313b741df41a6be9a049f71db6b
[2020-05-05 15:25:10] local.INFO: Session id: VNiBxr1gYYIA9Nr11x9c4JJArHOiKQScEGh2jkuc
[2020-05-05 15:25:10] local.ERROR: 7351b313b741df41a6be9a049f71db6b did not equal stored value <null>
EDIT2: I've tried the tutorial here which uses a slightly different approach using Laravel and the League Oauth library-- it has the exact same problem, the session ID is different between the two requests, meaning there's no way you'll ever get a match between the state keys.
I believe the problem lies with how you redirect to google.
Problem:
Laravel needs to run trough the whole request in order to persist values into the session.
By using exit; you are interrupting the request and therefore Laravel will not get the chance to persist the values into the session.
Solution:
By using the redirect() helper as suggested in the docs, Laravel will be able to complete the request.
elseif(empty($request->input('code'))) {
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
session(['oauth2state', $provider->getState()]);
Log::info('Storing provider state ' . session('oauth2state'));
return redirect($authUrl);
}
Explanation:
In Laravel you can decide when a middleware is run, from the docs:
Before & After Middleware
Whether a middleware runs before or after a request depends on the
middleware itself. For example, the following middleware would perform
some task before the request is handled by the application:
public function handle($request, Closure $next)
{
// Perform action
return $next($request);
}
However, this middleware would perform its task after the request is
handled by the application:
public function handle($request, Closure $next)
{
$response = $next($request);
// Perform action
return $response;
}
Now if we take a look at how Laravel persists the session data in the StartSession middleware, you can see here that Laravel tries to persist the data into the session after the request has been handled by the application, so by using exit;, die(); or dd(); your are stopping the script and Laravel never gets the opportunity to persist the values in the session.
protected function handleStatefulRequest(Request $request, $session, Closure $next)
{
// Before middleware
$request->setLaravelSession(
$this->startSession($request, $session)
);
$this->collectGarbage($session);
$response = $next($request);
// After middleware
$this->storeCurrentUrl($request, $session);
$this->addCookieToResponse($response, $session);
$this->saveSession($request);
return $response;
}
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}" );
}
This is my first time implementing OAuth to my projects. I found a walkthrough on github for laravel-5 oriceon/oauth-5-laravel. I followed all the steps correctly. However when I get to the controller function I get an error saying:
Call to undefined method Illuminate\Support\Facades\Request::get()
Here is my controller function:
public function loginWithFacebook(Request $request)
{
// get data from request
$code = $request->get('code');
// get fb service
$fb = \OAuth::consumer('Facebook');
// check if code is valid
// if code is provided get user data and sign in
if ( ! is_null($code))
{
// This was a callback request from facebook, get the token
$token = $fb->requestAccessToken($code);
// Send a request with it
$result = json_decode($fb->request('/me'), true);
$message = 'Your unique facebook user id is: ' . $result['id'] . ' and your name is ' . $result['name'];
echo $message. "<br/>";
//Var_dump
//display whole array.
dd($result);
}
// if not ask for permission first
else
{
// get fb authorization
$url = $fb->getAuthorizationUri();
// return to facebook login url
return redirect((string)$url);
}
}
In the app you can see that i did add the correct provider and alias:
'OAuth' => Artdarek\OAuth\Facade\OAuth::class,
Artdarek\OAuth\OAuthServiceProvider::class,
In my view I call the route that leads to the correct controller function and I keep arriving to this error. What could it be that does this? Should the function be calling to the provider or something? Thanks for looking at this Stack!
First up, I hope your view isn't calling a route- that's backwards. Routes are used immediately to determine the controller, which is then used to determine and respond with the proper view.
... That aside, Request is the name of a facade in Laravel. That's why the error message says it's looking for the get() method on the Illuminate\Support\Facades\Request class. You'll want to namespace the Request class you're using so that it's able to use the correct get() method. Depending on your version, I do this with use Illuminate\Http\Request; at the top of my controller file (immediately after the namespace declaration for the controller).
I have made a web application that uses SOAP exchanges to get data from a Web API. This was initially done in a procedural way and I'm now trying to move it into a Laravel framework. I have a view set up to display to the user if the SOAP Response is "Request denied by Throttle server" but I don't know how to check for that particular error. Here is the Class:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// call 'setCookie' method on '$search_client' storing SID (Session ID) as the response (value) given from the 'authenticate' method
// check if an SID has been set, if not it means Throttle server has stopped the query, therefore display error message
if (isset($auth_response->return)) {
$search_client->__setCookie('SID',$auth_response->return);
} else {
return Redirect::route('throttle');
}
}
}
The problem is that it throws the "Request denied by Throttle server" default Laravel error at $auth_response = $auth_client->authenticate(); before it gets to the if statement that checks if a value (SessionID) has been returned by the SOAP Request. It didn't do this when it was set up in a procedural way for some reason.
The if statement checks if a value has been returned from the authenticate() method and if it has, assigns it (SessionID) to the cookie of the search client to authorise searches. Otherwise it displays a custom error message.
I have tried using is_soap_fault but that doesn't catch it because it isn't technically a soap fault. I've also tried removing the line causing the problem and changing the if statement to:
if (isset($auth_client->authenticate()->return) {...
But that just causes the default Laravel SoapFault page too. The return Redirect::route('throttle') displays a custom error page to the user, saved as throttle.blade.php.
Anyone know how I can test for the throttle error?
Never mind, found answer here: Catching an exception when creating a new SoapClient properly.
I'll post my amended code anyway just in case it's of any use to anyone else in future:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
try {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// add SID (SessionID) returned from authenticate() to cookie of search client
$search_client->__setCookie('SID', $auth_response->return);
} catch (\SoapFault $e) {
return Redirect::route('throttle');
}
}
}
I currently trying to test a symfony app that has secured pages, using functional tests.
Scenario
Log in as a user that is a superadmin (thus bypassing any credential check)
Browse to a secured page (here displaying a list of documents)
Make some various tests to check various requirements.
Problem
When I browse the secured page, instead of a 200 HTTP code, I get a 401 "Unauthorized" HTTP code, meaning I'm trying to access a page without being authenticated/without having the right credentials.
Even if I log as a normal user, that have the proper symfony credentials, I still get a 401 error.
Code used
//CapWebndfTestFunctional.class.php
class CapWebndfTestFunctional extends sfTestFunctional
{
/**
* Login as a user
* #param string $username User's username
*/
public function signInAs($username)
{
$user = Doctrine_Core::getTable('sfGuardUser')->findOneByUsername($username);
//force the context to be created
$this->browser->getContext(true)->getUser()->signIn($user);
$this->info(sprintf('Signin user using username "%s"', $username));
if ($user->getIsSuperAdmin())
{
$this->info('Beware, you are logged as a superadmin!');
}
return $this;
}
}
Test suite:
<?php
$browser = new CapWebndfTestFunctional(new sfBrowser());
$browser->
signInAs('superadmin')->
with('user')->begin()->
isAuthenticated()-> //ensure that I'm well authenticated
end()->
get('/document')-> //a secured page
with('request')->begin()->
isParameter('module', 'document')->
isParameter('action', 'index')->
end()->
with('response')->begin()->
isStatusCode(200)-> //as superadmin is a sfGuard superadmin, this should be 200;
end()->
// Here some other tests...
/* Outputs :
clem#ubuntu:/var/www/cap_webndf/trunk$ php symfony test:functional backend documentActions
> Signin user using username "superadmin"
> Beware, you are logged as a superadmin!
ok 1 - user is authenticated
# get /document
ok 2 - request parameter module is document
ok 3 - request parameter action is index
not ok 4 - status code is 200
# Failed test (./lib/vendor/symfony/lib/test/sfTesterResponse.class.php at line 412)
# got: 401
# expected: 200
*/
I'm really stuck on this one, so thank you for any help.
Edit: If you prefer, here is the used code on a gist
EDIT: What is the difference between going to the login page and sending a (fake) post request, and directly signin in using the dedicated sfGuard method?
I don't think you can use SignIn() like that as it would not set the cookie within the browser so on the next page you would be logged out again.
Whenever I have done functional testing with users I have gone to the login page and entered the username/password of the user and logged in as them.
I haven't tested this but something like this should work, I normally use test users and know their password
//CapWebndfTestFunctionnal.class.php
class CapWebndfTestFunctionnal extends sfTestFunctional
{
/**
* Login as a user
* #param string $username User's username
*/
public function signInAs($username)
{
$tempPassword = rand(1000, 9999999);
$user = Doctrine_Core::getTable('sfGuardUser')->findOneByUsername($username);
$oldPassword = $user->getPassword();
$user->setPassword($tempPassword);
$user->save();
$this->info(sprintf('Signin user using username "%s"', $username));
$this->post('/login', array('username' => $user->getUsername(), 'password' => $tempPassword))->isRedirected();
if ($user->getIsSuperAdmin())
{
$this->info('Beware, you are logged as a superadmin!');
}
$user->setPasswordHash($oldPassword);
$user->save();
return $this;
}
}