Passing IIS Windows Authentication details from PHP login page to secured services

15661
15
Jump to solution
04-23-2014 09:47 AM
by Anonymous User
Not applicable
I am using a PHP login page (in front of a map application) using Windows Authentication through IIS 7.5, and Active Directory manages the allowed users and their credentials. This login page starts a PHP Session to unlock all subsequent secure sites. However our web services need to be secured through our ArcGIS Server, so as users move onward they will be challenged for credentials again by the ArcGIS Server.

I am a newb and stuck at how the 'Negotiate' authentication from IIS (on the PHP site) can be used to also unlock the 'Web Tier' authentication on the ArcGIS Server so that users only enter their credentials once on the initial login page. This will be necessary for users not on the intranet. Is it possible? Maybe I should use this approach: hard code token.

Thanks for any thoughts!
0 Kudos
1 Solution

Accepted Solutions
RaymondGoins
Occasional Contributor
We don't use a windows domain here but after some reading it seems the best solution is to use ldap. PHP requires you just un-comment the ldap.dll in php.ini and ldap is on by default in active directory. That being said I would put my functions in a separate file so that they may be used throughout your application.

functions.php
[PHP]
function login_check()
{
  if (isset($_SESSION['IWP_loggedIn']) && $_SESSION['IWP_loggedIn'] === true && isset($_SESSION["IWPUser"])) {
    return true;
  } else {
   return false;
  }
}

function do_login($username, $password, $redirect)
{
  $un = $username; // This may need to change. you may have to append the domain ie. mydomain\username
  $pw = $password;
  if(empty($un)) {
    header("Location: http://example.com/login.php?error=Username cannot be blank");
    exit();
  }
  if(empty($pw)) {
    header("Location: http://example.com/login.php?error=Username cannot be blank");
    exit();
  }
  $server = "ldaps://yourservernamehere"; //IP or name of server here
  //echo $server;
  if (!($ldap = ldap_connect($server))) {
    header("Location: http://example.com/login.php?error=Could Not Connect To Server"); // can change these error messages
    exit();
  }
  if (!ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3)) {
    header("Location: http://example.com/login.php?error=Could Not Connect To Server");
    exit();
  }
  if (!($res = @ldap_bind($ldap, $un, $pw))) {
    header("Location: http://example.com/login.php?error=Could Not Connect To Server");
    exit();
  } else {
    $_SESSION['IWP_loggedIn'] = true;
    $_SESSION["IWPUser"] = $username;
    header("Location: ".$redirect);
  }
}

function do_logout($redirect)
{
  session_destroy();
  header("Location: ".$redirect);
}[/PHP]

now you can require this file at the top of all your pages so that the functions can be used.
login.php
[HTML]<?php
  $error = isset($_GET['error']) ? $_GET['error'] : "";
?>

<style type="text/css">
/* Login Form style  */
#loginform {
  font-size: 14px;
  text-align: center;
  width: 480px;
  position: fixed;
  /*display: none;*/
  background-color: #EEEEEE;
  border-radius: 13px;
  border: 2px solid #3399CC;
  top: 40%;
  left: 36%;
  z-index: 200;
  box-shadow: 10px 10px 10px black;
}

#loginform .form_header
{
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  border-bottom: none;
  width: 480px;
  height: 30px;
  line-height: 19px;
  vertical-align: middle;
  background-color: #3399CC;
  text-decoration: none;
  font-family: Times New Roman, Serif;
  font-weight: 900;
  font-size:  18px;
  text-align: left;
  padding-top: 5px;
  color:   #FFFFFF;
  cursor:  default;
}

#loginform .form_body
{
  width: 400px;
  border: 1px solid black;
  background-color: #FFFFFF;
}

#loginform .status
{
  width: 400px;
  margin: 10px auto 0 auto;
}

#loginform input {
  margin: 5px;
  padding: 0px;
  float: left;
  border: 1px solid #cdcdcd;
  background-color: white;
  border-radius: 2px;
}

/* 'Login' Button */
#submit {
  margin: 5px;
  padding: 0px;
  float: left;
  width: 50px;
  background-color: white;
}
</style>
<div id="loginform" style="" >
  <div class="form_header" id="popup_drag" >
    <span style="margin-left:5px;">Employee Login</span>
  </div>
  <div class="status">
    <fieldset>
    <legend align="center">Authentication</legend>
    <form id="login" name="login" method="post" action="/do_login.php" >
      <input type="hidden" name="redirect" value="/gis"/>
      <table align="center" width="300" border="0">
      <tr>
        <td width="80">Username</td>
        <td><input id="name" type="text" name="username"></td>
      </tr>
      <tr>
        <td>Password</td>
        <td><input type="password" name="password"></td>
      </tr>
      <tr>
        <td> </td>
        <td><input id="submit" class="login-submit" type="submit" name="submit" value="Login"></td>
      </tr>
      </table>
    </form>
    </fieldset>
  </div><br><span style="color:red;"><?php echo $error;?></span>
</div>[/HTML]

do_login.php
[PHP]
session_start();
require('functions.php');
// Process login
$postdata = $_POST;
do_login($postdata['username'], $postadat['password'], $postdata['redirect']);[/PHP]
Very simple. You can add in some messages if you like.

Then at the top of all your pages just run a login check
[PHP]session_start();
require('functions.php');
if(login_check() === false)
{
  header("Location: http://example.com/login.php");
  exit();
} else {
// rest of your page below
}
[/PHP]

I have not tested every part of this but once you implement it you can post any errors here and I will help out best I can.
Don't forget to start all your pages with session_start(); just incase I forgot to add it to any of the pages. Also remember to change all your header locations.

Hope that gets you going.

Ray

View solution in original post

0 Kudos
15 Replies
RaymondGoins
Occasional Contributor
Do you have any code for this as of yet?? If you do please post. I am an experience PHP coder and should be able to help.

Ray

*EDIT*

try taking a look here
http://www.iis.net/learn/application-frameworks/install-and-configure-php-on-iis/enable-php-applicat...
0 Kudos
by Anonymous User
Not applicable
This is the login page. I am stuck at where to capture login information that IIS passes back during Windows Authentication:

<!DOCTYPE html>
<html>
 <head>
  <title>Secure Access</title>
 </head>

 <body>
  <?php
  // Start session to track this users identity
  session_start();
  // Make sure they aren't already in a session; if so, redirect to GIS Home
  // IWP_loggedIn is boolean, 'true'; if there is a session, credentials are already stored
  if ($_SESSION['IWP_loggedIn']) {
   // Redirect to the secure content (ArcGIS JavaScript app with secured ArcGIS Server services)
   // header("Location: http://example.com/gis.php");
   // TEST MODE: print the authorized user name
   echo $_SERVER['LOGON_USER'];
   // Make sure that code below does not get executed when we redirect
   exit();

  } else {
   // If no session is started and no authentication has been performed
   if (empty($_SERVER["LOGON_USER"])) {
    // If user is not in intranet, or browser is not enabled for SSO
    // IIS/browser prompt for credentials; this message is a stand-in for now
    echo "<h1>Please login with your credentials to access GIS data</h1>";
    // Create session logged-in variable to track user
    $_SESSION['IWP_loggedIn'] = TRUE;
    // Store credentials (how do I pass on username/password or key to the next site?)
    $_SESSION["IWPUser"] = $_SERVER["LOGON_USER"];
    //Redirect, credentials passed via PHP session
    // header("Location: http://example.com/gis.php");
    // TEST MODE: print the authorized user name
    echo $_SERVER['LOGON_USER'];
    // Make sure that code below does not get executed when we redirect
    exit();
    }

   // If user is on intranet and browser is enabled for SSO, automatic login occurs
   // "LOGON USER" field will be filled, auto-redirect to map
   if (!empty($_SERVER["LOGON_USER"])) {
    echo "Oh Joy!!!";
    // Create session logged-in variable to track user
    $_SESSION['IWP_loggedIn'] = TRUE;
    // Store credentials (how do I pass on username/password or key to the next site?)
    $_SESSION["IWPUser"] = $_SERVER["LOGON_USER"];
    // Redirect, credentials passed via PHP session
    // header("Location: http://example.com/gis.php");
    // TEST MODE: print the authorized user name
    echo $_SERVER['LOGON_USER'];
    // Make sure that code below does not get executed when we redirect
    exit();
   }
  }
  ?>
 </body>
</html>
0 Kudos
by Anonymous User
Not applicable
This is the PHP snippet in front of each JavaScript app with secured services. This code is to prevent users from directly typing in the app url and getting to the map instead of going through the login page. I need to add in an authentication part.

<?php
// Start session to track this users identity
session_start();
// Make sure they are logged in, if not redirect to login
// IWP_loggedIn is boolean, 'true'
if (!$_SESSION['IWP_loggedIn']) {
 // Redirect to the secure content
 header("Location: http://example.com/login.php");
 // Make sure that code below does not get executed when we redirect
 exit();
}
else {
 //talk to ArcGIS Server/Web Adaptor to tell it the users credentials
}
?>
0 Kudos
by Anonymous User
Not applicable
Raymond the link you provided as been very helpful, I think I need to be looking into Forms Authentication (not Windows)
0 Kudos
RaymondGoins
Occasional Contributor
We don't use a windows domain here but after some reading it seems the best solution is to use ldap. PHP requires you just un-comment the ldap.dll in php.ini and ldap is on by default in active directory. That being said I would put my functions in a separate file so that they may be used throughout your application.

functions.php
[PHP]
function login_check()
{
  if (isset($_SESSION['IWP_loggedIn']) && $_SESSION['IWP_loggedIn'] === true && isset($_SESSION["IWPUser"])) {
    return true;
  } else {
   return false;
  }
}

function do_login($username, $password, $redirect)
{
  $un = $username; // This may need to change. you may have to append the domain ie. mydomain\username
  $pw = $password;
  if(empty($un)) {
    header("Location: http://example.com/login.php?error=Username cannot be blank");
    exit();
  }
  if(empty($pw)) {
    header("Location: http://example.com/login.php?error=Username cannot be blank");
    exit();
  }
  $server = "ldaps://yourservernamehere"; //IP or name of server here
  //echo $server;
  if (!($ldap = ldap_connect($server))) {
    header("Location: http://example.com/login.php?error=Could Not Connect To Server"); // can change these error messages
    exit();
  }
  if (!ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3)) {
    header("Location: http://example.com/login.php?error=Could Not Connect To Server");
    exit();
  }
  if (!($res = @ldap_bind($ldap, $un, $pw))) {
    header("Location: http://example.com/login.php?error=Could Not Connect To Server");
    exit();
  } else {
    $_SESSION['IWP_loggedIn'] = true;
    $_SESSION["IWPUser"] = $username;
    header("Location: ".$redirect);
  }
}

function do_logout($redirect)
{
  session_destroy();
  header("Location: ".$redirect);
}[/PHP]

now you can require this file at the top of all your pages so that the functions can be used.
login.php
[HTML]<?php
  $error = isset($_GET['error']) ? $_GET['error'] : "";
?>

<style type="text/css">
/* Login Form style  */
#loginform {
  font-size: 14px;
  text-align: center;
  width: 480px;
  position: fixed;
  /*display: none;*/
  background-color: #EEEEEE;
  border-radius: 13px;
  border: 2px solid #3399CC;
  top: 40%;
  left: 36%;
  z-index: 200;
  box-shadow: 10px 10px 10px black;
}

#loginform .form_header
{
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  border-bottom: none;
  width: 480px;
  height: 30px;
  line-height: 19px;
  vertical-align: middle;
  background-color: #3399CC;
  text-decoration: none;
  font-family: Times New Roman, Serif;
  font-weight: 900;
  font-size:  18px;
  text-align: left;
  padding-top: 5px;
  color:   #FFFFFF;
  cursor:  default;
}

#loginform .form_body
{
  width: 400px;
  border: 1px solid black;
  background-color: #FFFFFF;
}

#loginform .status
{
  width: 400px;
  margin: 10px auto 0 auto;
}

#loginform input {
  margin: 5px;
  padding: 0px;
  float: left;
  border: 1px solid #cdcdcd;
  background-color: white;
  border-radius: 2px;
}

/* 'Login' Button */
#submit {
  margin: 5px;
  padding: 0px;
  float: left;
  width: 50px;
  background-color: white;
}
</style>
<div id="loginform" style="" >
  <div class="form_header" id="popup_drag" >
    <span style="margin-left:5px;">Employee Login</span>
  </div>
  <div class="status">
    <fieldset>
    <legend align="center">Authentication</legend>
    <form id="login" name="login" method="post" action="/do_login.php" >
      <input type="hidden" name="redirect" value="/gis"/>
      <table align="center" width="300" border="0">
      <tr>
        <td width="80">Username</td>
        <td><input id="name" type="text" name="username"></td>
      </tr>
      <tr>
        <td>Password</td>
        <td><input type="password" name="password"></td>
      </tr>
      <tr>
        <td> </td>
        <td><input id="submit" class="login-submit" type="submit" name="submit" value="Login"></td>
      </tr>
      </table>
    </form>
    </fieldset>
  </div><br><span style="color:red;"><?php echo $error;?></span>
</div>[/HTML]

do_login.php
[PHP]
session_start();
require('functions.php');
// Process login
$postdata = $_POST;
do_login($postdata['username'], $postadat['password'], $postdata['redirect']);[/PHP]
Very simple. You can add in some messages if you like.

Then at the top of all your pages just run a login check
[PHP]session_start();
require('functions.php');
if(login_check() === false)
{
  header("Location: http://example.com/login.php");
  exit();
} else {
// rest of your page below
}
[/PHP]

I have not tested every part of this but once you implement it you can post any errors here and I will help out best I can.
Don't forget to start all your pages with session_start(); just incase I forgot to add it to any of the pages. Also remember to change all your header locations.

Hope that gets you going.

Ray
0 Kudos
by Anonymous User
Not applicable
Ray that's great, I really appreciate it. I might use this and skip trying to auto-authenticate via Windows, just to simplify.

The login is working but I'm having trouble carrying the session variables over. When I put the PHP code in front of a protected website to run the 'login_check' function, I am able to log in (login.php) and get redirected back to the page but the $_SESSION is empty (it's failing at 'login_check' function), and I am then routed back to login again. I put 'session_start()" at the top of every page now, but its not seeming to store it.
0 Kudos
RaymondGoins
Occasional Contributor
Not sure if you copied and pasted everything but looking through my code I made a typo. 😞

do_login.php
[PHP]session_start();
require('functions.php');
// Process login
$postdata = $_POST;
do_login($postdata['username'], $postdata['password'], $postdata['redirect']);[/PHP]

This actually sends you in a loop because you would never get authenticated so the session is never set and you end up on the login page forever.

Let me know.

Ray
0 Kudos
RaymondGoins
Occasional Contributor
Also you do not need to put session_start() at the top of the functions.php page. since this is a required page the session has already been started by the parent page.

Ray
0 Kudos
by Anonymous User
Not applicable
I did catch that typo. I don't see the problem...If I don't redirect and just echo out the $_SESSION["IWPUser"] at the end of the login function, it shows up fine. As soon as I try to pass it to another page it disappears.
0 Kudos