Create login system, add i18n, add 2fa auth

V2_Rewrite
Skylar Ittner 7 years ago
parent b16da81513
commit 2caec9b758

Binary file not shown.

@ -0,0 +1,6 @@
<?php
require_once __DIR__ . "/required.php";
dieifnotloggedin();
?>
Home

@ -1,17 +1,113 @@
<?php
require_once __DIR__ . "/required.php";
require_once __DIR__ . "/lib/login.php";
/* Authenticate user */
$userpass_ok = false;
$multiauth = false;
if ($VARS['progress'] == "1") {
if (authenticate_user($VARS['username'], $VARS['password'])) {
switch (get_account_status($VARS['username'])) {
case "LOCKED_OR_DISABLED":
$alert = lang("account locked", false);
break;
case "TERMINATED":
$alert = lang("account terminated", false);
break;
case "CHANGE_PASSWORD":
$alert = lang("password expired", false);
case "NORMAL":
$userpass_ok = true;
break;
case "ALERT_ON_ACCESS":
sendAlertEmail($VARS['username']);
$userpass_ok = true;
break;
}
if ($userpass_ok) {
if (userHasTOTP($VARS['username'])) {
$multiauth = true;
} else {
doLoginUser($VARS['username']);
header('Location: home.php');
die("Logged in, go to home.php");
}
}
} else {
$alert = lang("login incorrect", false);
}
} else if ($VARS['progress'] == "2") {
if (verifyTOTP($VARS['username'], $VARS['authcode'])) {
doLoginUser($VARS['username']);
header('Location: home.php');
die("Logged in, go to home.php");
} else {
$alert = lang("2fa incorrect", false);
}
}
?>
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<meta charset="UTF-8">
<title></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" contgreent="width=device-width, initial-scale=1">
<title><?php echo SITE_TITLE; ?></title>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/app.css" rel="stylesheet">
</head>
<body>
<?php
// put your code here
?>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4 col-sm-offset-3 col-md-offset-4 col-lg-offset-4">
<div>
<img class="img-responsive banner-image" src="static/img/banner.png" />
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php lang("sign in"); ?></h3>
</div>
<div class="panel-body">
<form action="" method="POST">
<?php
if (!is_empty($alert)) {
?>
<div class="alert alert-danger">
<?php echo $alert; ?>
</div>
<?php
}
if ($multiauth != true) {
?>
<input type="text" class="form-control" name="username" placeholder="<?php lang("username"); ?>" required="required" /><br />
<input type="password" class="form-control" name="password" placeholder="<?php lang("password"); ?>" required="required" /><br />
<input type="hidden" name="progress" value="1" />
<?php
} else if ($multiauth) {
?>
<div class="alert alert-info">
<?php lang("2fa prompt"); ?>
</div>
<input type="text" class="form-control" name="authcode" placeholder="<?php lang("authcode"); ?>" required="required" /><br />
<input type="hidden" name="progress" value="2" />
<input type="hidden" name="username" value="<?php echo $VARS['username']; ?>" />
<?php
}
?>
<button type="submit" class="btn btn-primary">
<?php lang("continue"); ?>
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="static/js/jquery-3.2.1.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
</body>
</html>
</html>

@ -0,0 +1,15 @@
<?php
define("STRINGS", [
"sign in" => "Sign In",
"username" => "Username",
"password" => "Password",
"continue" => "Continue",
"authcode" => "Authentication code",
"2fa prompt" => "Enter the six-digit code from your mobile authenticator app.",
"2fa incorrect" => "Authentication code incorrect.",
"login incorrect" => "Login incorrect.",
"account locked" => "This account has been disabled. Contact technical support.",
"password expired" => "You must change your password before continuing.",
"account terminated" => "Account terminated. Access denied."
]);

@ -0,0 +1,77 @@
<?php
use Base32\Base32;
use OTPHP\TOTP;
/**
* Send an alert email to the system admin
*
* Used when an account with the status ALERT_ON_ACCESS logs in
* @param String $username the account username
*/
function sendAlertEmail($username) {
// TODO: add email code
}
/**
* Setup $_SESSION values to log in a user
* @param string $username
*/
function doLoginUser($username) {
global $database;
$userinfo = $database->select('accounts', ['email', 'uid', 'realname'], ['username' => $username])[0];
$_SESSION['username'] = $username;
$_SESSION['uid'] = $userinfo['uid'];
$_SESSION['email'] = $userinfo['email'];
$_SESSION['realname'] = $userinfo['realname'];
$_SESSION['loggedin'] = true;
}
/**
* Check if a user has TOTP setup
* @global $database $database
* @param string $username
* @return boolean true if TOTP secret exists, else false
*/
function userHasTOTP($username) {
global $database;
$secret = $database->select('accounts', 'authsecret', ['username' => $username])[0];
if (is_empty($secret)) {
return false;
}
return true;
}
/**
* Generate and store a TOTP secret for the given user.
* @param string $username
* @return string OTP provisioning URI (for generating a QR code)
*/
function newTOTP($username) {
global $database;
$secret = random_bytes(20);
$encoded_secret = Base32::encode($secret);
$userdata = $database->select('accounts', ['email', 'authsecret'], ['username' => $username])[0];
$totp = new TOTP($userdata['email'], $encoded_secret);
$totp->setIssuer(SYSTEM_NAME);
$database->update('accounts', ['authsecret' => $encoded_secret], ['username' => $username]);
return $totp->getProvisioningUri();
}
/**
* Verify a TOTP multiauth code
* @global $database
* @param string $username
* @param int $code
* @return boolean true if it's legit, else false
*/
function verifyTOTP($username, $code) {
global $database;
$userdata = $database->select('accounts', ['email', 'authsecret'], ['username' => $username])[0];
if (is_empty($userdata['authsecret'])) {
return false;
}
$totp = new TOTP(null, $userdata['authsecret']);
echo $userdata['authsecret'] . ", " . $totp->now() . ", " . $code;
return $totp->verify($code);
}

@ -13,6 +13,8 @@ require __DIR__ . '/vendor/autoload.php';
// Settings file
require __DIR__ . '/settings.php';
require __DIR__ . '/lang/' . LANGUAGE . ".php";
function sendError($error) {
die("<!DOCTYPE html><html><head><title>Error</title></head><body><h1 style='color: red; font-family: sans-serif; font-size:100%;'>" . htmlspecialchars($error) . "</h1></body></html>");
}
@ -65,6 +67,20 @@ function is_empty($str) {
return (is_null($str) || !isset($str) || $str == '');
}
function lang($key, $echo = true) {
if (array_key_exists($key, STRINGS)) {
$str = STRINGS[$key];
} else {
$str = $key;
}
if ($echo) {
echo $str;
} else {
return $str;
}
}
/**
* Add a user to the system. /!\ Assumes input is OK /!\
* @param string $username Username, saved in lowercase.
@ -113,6 +129,41 @@ function user_exists($username) {
return $database->has('accounts', ['username' => $username, "LIMIT" => QUERY_LIMIT]);
}
/**
* Checks the given credentials against the database.
* @param string $username
* @param string $password
* @return boolean True if OK, else false
*/
function authenticate_user($username, $password) {
global $database;
if (is_empty($username) || is_empty($password)) {
return false;
}
if (!user_exists($username)) {
return false;
}
$hash = $database->select('accounts', ['password'], ['username' => $username, "LIMIT" => 1])[0]['password'];
return (comparePassword($password, $hash));
}
function get_account_status($username) {
global $database;
$statuscode = $database->select('accounts', [
'[>]acctstatus' => [
'acctstatus' => 'statusid'
]
], [
'accounts.acctstatus',
'acctstatus.statuscode'
], [
'username' => $username,
"LIMIT" => 1
]
)[0]['statuscode'];
return $statuscode;
}
/**
* Checks the given credentials to see if they're legit.
* @param string $username

@ -18,6 +18,9 @@ define("LDAP_BASEDN", "ou=users,dc=example,dc=com");
define("SITE_TITLE", "Netsyms Business Apps :: Single Sign On");
// Used to identify the system in OTP and other places
define("SYSTEM_NAME", "Netsyms SSO Demo");
define("COPYRIGHT_NAME", "Netsyms Technologies");
// For supported values, see http://php.net/manual/en/timezones.php
@ -26,5 +29,8 @@ define("TIMEZONE", "America/Denver");
// Base URL for site links.
define('URL', 'http://localhost:8000/');
// See lang folder for language options
define('LANGUAGE', "en_us");
// Maximum number of rows to get in a query.
define("QUERY_LIMIT", 1000);

@ -0,0 +1,3 @@
.banner-image {
margin: 2em 0em;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,32 @@
<?php
require __DIR__ . "/required.php";
require __DIR__ . "/lib/login.php";
use Endroid\QrCode\QrCode;
use OTPHP\TOTP;
if ($_GET['show'] == '1') {
$totp = new TOTP(
"admin@netsyms.com", // The label (string)
"ZBUJDTW5D5E6KBMDICAJSKRCX6VGQZCZ" // The secret encoded in base 32 (string)
);
echo "Current OTP: " . $totp->now();
die();
} else {
$user = "skylarmt";
$totp = newTOTP($user);
// Create a QR code
$qrCode = new QrCode($totp);
$qrCode->setSize(300);
// now we can output the QR code
header('Content-Type: ' . $qrCode->getContentType(QrCode::IMAGE_TYPE_PNG));
$qrCode->render(null, 'png');
}
Loading…
Cancel
Save