# PHP example

In this example, we add authentication and access control to a PHP application.

To demonstrate both authentication and access control, our application will have several routes:

Route Description File
/ Home page (index.php) (opens new window)
/protected.php User dashboard, for logged in users only (opens new window)
/admin.php Admin dashboard, for admins only (opens new window)
/login.php Login page (opens new window)
/reset.php Password reset page (opens new window)

We cover each route below, along with an interactive sample at the end.

# Sample on PHP sandbox

Sandbox with code (opens new window)

Live demo (opens new window) (you may need to visit the sandbox first to "wake up" the demo server)

# PHP authentication

At a high level, PHP authentication is structured in 2 parts:

  1. Send an initial request to Userfront to get the JWT access token and save it as a cookie. This is what the signup and login forms do.

  2. Send the JWT access token as a cookie to your server with each subsequent request. To verify the JWT access token, we will use the PHP-JWT library (opens new window)

PHP authentication diagram

# Logged in vs. logged out

When someone new visits the Home page, they should see a signup form.

When a user is logged in and visits the Home page, they should see navigation to visit the dashboard, as well as a button to logout.

# Redirects

In the event that the user does not have access to a page, we want to redirect them:

If the user visits Redirect to
/protected.php when not logged in /login.php
/admin.php when not an admin /protected.php

TIP

With Userfront, a logged in user will have a JWT access token saved in their cookies as .

In this example, we use access.pn4qwpby

# Public route

For showing and hiding simple things that are not sensitive, we can check whether the user has a JWT access token.

<!-- /index.php -->

<?php
// Read the JWT access token from the cookies
$tenantId = 'pn4qwpby';

if (isset($_COOKIE['access_' . $tenantId])) {
    $jwt = $_COOKIE['access_' . $tenantId];
}

$hasAccessToken = isset($jwt);
?>

If the user does have a token present, we can display items as though they are logged in, and if not, we can display items as though they are logged out.

Note

Checking for the presence of the JWT access token should only be used for showing or hiding non-sensitive items. For sensitive data, verify the JWT access token as described below.

For this site, we show a link to the dashboard if logged in, and we show the signup form if logged out.

<!-- /index.php -->

<?php
if ($hasAccessToken) {
    echo '<ul>';
    echo '<li><a href="/protected.php">Dashboard</a></li>';
    echo '<li><a href="/reset.php">Password reset</a></li>';
    echo '</ul>';
    echo '<br><button>Logout</button>';
} else {
    echo '<a href="/login.php">Login</a>';
    echo '<!-- Signup form -->';
    echo '<div id="userfront-naanob"></div>';
}
?>

For any sensitive information, we want to protect our routes as described next.

# Preview

Logged in home page:

Logged out home page:

# Protected route

To protect a route in PHP, we need to verify that the JWT access token is valid and has not expired.

To verify our JWT access token, we can use the account's JWT public key.

We will use the open source PHP-JWT (opens new window) library to verify our JWT access tokens. We can install this library with composer:

composer require firebase/php-jwt

TIP

You can find your JWT public key in the Settings section of your dashboard.

Be sure to use your test mode key when in test mode, and your live mode key when in live mode.

If the JWT access token is not present or is invalid, we redirect to /login.php. Otherwise, we display the dashboard page.

<!-- /protected.php -->

<?php
// Use the PHP-JWT library https://github.com/firebase/php-jwt
// Installed first with "composer require firebase/php-jwt"
require __DIR__ . '/vendor/autoload.php';
use \Firebase\JWT\JWT;

// Read the JWT access token from the cookies
$tenantId = 'pn4qwpby';
$jwt = $_COOKIE['access_' . $tenantId];

// If the JWT access token is not set, redirect to login page
if (!isset($jwt)) {
    header("Location: /login.php");
    die();
}

$publicKey = "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs5fuiDLiZICIMnN3OAIP
Dv/IQIJSJ/IIPG8Q/uYxIx10Sm1SiykggrQIKYCAZoyqxxq8/mJhdXsMm1vWK+e3
kN0KJNylB4+/kNVQOgxoT3+qlas4ieZb1/p3eIlFMrNjHRqgFmDd75z+L0k4bVJ/
2Bh2kCxx7d9cS0A7cyEJ7ZFq6FeiNRUxhhpdwzMm5+A8WK8urYLIO6yfe4Cast1r
ToBrleIyXbs32fTezmTXA3IsoA1Cj/XADqsloV2FR/xA5DEaqgo3I6OmqFX8xiG6
f6IMneObVsDphEnDTda6IxPqxnfsetcu8gL0sNzsxxKe2+/FlNNRKdp7Jq8PGy2q
dAJxFXfcpM+TqcOtF4UIfPcZ6rDm/9McJ9uUj4nNKPilgZhsxeRGwaXJVFgaHB+h
+BYGWRQm8WBXBj4aMyyf2/T4mV/PeSqXY+2N28rEUjhw2E3rMCF4gHiauVv8qn19
hWEnKwk8IF1hJLkbtXLhF41KPCVC++x7EtQ27t1RE5+KAlH4bm3+CRdHqkjAyzCt
90m+3iTB5ThUk9mBZk3Ozr1+w7vvjUlyUSe0/hC5MLc1B7ee/bu2JBBRCcgXziBt
YHqFCRKQz3nc1vQXiYDhUgjZRq74mkzFN6H+X+Y9Sk3VUHJazvsGmmwEy1Cdw6kf
dgmH+hE/hlRBdk6licZic0kCAwEAAQ==
-----END PUBLIC KEY-----";

// Verify the JWT access token using the public key.
// If it is not valid, remove all cookies and redirect to /login.php
try {
    $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
    $roles = $decoded->authorization->$tenantId->roles;
    $isAdmin = in_array('admin', $roles);
} catch (Exception $e) {
    setcookie("access_" . $tenantId, "", time() - 3600);
    setcookie("id_" . $tenantId, "", time() - 3600);
    header("Location: /login.php");
    die();
}
?>

<html>
<head>
    <meta charset="utf-8">
    <title>Dashboard</title>

    <script id="Userfront-script">
        (function(m,o,d,u,l,a,r,i,z,e) {
            u[m]={rq:[],ready:function(j){u[m].rq.push(j);},m:m,o:o,d:d,r:r};function j(s){return encodeURIComponent(btoa(s));}z=l.getElementById(m+"-"+a);r=u.location;
            e=[d+"/page/"+o+"/"+j(r.pathname)+"/"+j(r.host)+"?t="+Date.now(),d];e.map(function(w){i=l.createElement(a);i.defer=1;i.src=w;z.parentNode.insertBefore(i,z);});u.amvartem=m;
        })("Userfront", "pn4qwpby", "https://cdn.userfront.com/toolkit",window,document,"script");
    </script>
</head>
<body>

<a href="/">Home</a> / Dashboard
<br>
<br>

<?php if ($isAdmin) { echo '<a href="/admin.php">Admin page</a><br><br>'; } ?>

This page is only for users who are logged in. The current user has the following JWT access token payload:

<br>
<pre><?php echo print_r((array) $decoded, true); ?></pre>
<br>

<button onclick="Userfront.logout()">Logout</button>

</body>
</html>

There are 2 forms of authentication here:

  1. If the JWT access token is not present in the cookies, redirect to /login.php:
if (!isset($jwt)) {
    header("Location: /login.php");
    die();
}
  1. If the JWT access token is not valid for any reason, remove all Userfront cookies and redirect to /login.php:
try {
    $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
    <!-- ... -->
} catch (Exception $e) {
    setcookie("access_" . $tenantId, "", time() - 3600);
    setcookie("id_" . $tenantId, "", time() - 3600);
    header("Location: /login.php");
    die();
}

# Preview

Logged in dashboard page:

# Admin route

If you haven't already, install the JWT-PHP (opens new window) library:

composer require firebase/php-jwt

The admin route is similar to the protected route, except in addition to verifying the JWT access token, we also check that the JWT access token has an admin role.

<!-- /admin.php -->

<?php
// Use the PHP-JWT library https://github.com/firebase/php-jwt
// Installed first with "composer require firebase/php-jwt"
require __DIR__ . '/vendor/autoload.php';
use \Firebase\JWT\JWT;

// Read the JWT access token from the cookies
$tenantId = 'pn4qwpby';
$jwt = $_COOKIE['access_' . $tenantId];

// If the JWT access token is not set, redirect to login page
if (!isset($jwt)) {
    header("Location: /login.php");
    die();
}

$publicKey = "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs5fuiDLiZICIMnN3OAIP
Dv/IQIJSJ/IIPG8Q/uYxIx10Sm1SiykggrQIKYCAZoyqxxq8/mJhdXsMm1vWK+e3
kN0KJNylB4+/kNVQOgxoT3+qlas4ieZb1/p3eIlFMrNjHRqgFmDd75z+L0k4bVJ/
2Bh2kCxx7d9cS0A7cyEJ7ZFq6FeiNRUxhhpdwzMm5+A8WK8urYLIO6yfe4Cast1r
ToBrleIyXbs32fTezmTXA3IsoA1Cj/XADqsloV2FR/xA5DEaqgo3I6OmqFX8xiG6
f6IMneObVsDphEnDTda6IxPqxnfsetcu8gL0sNzsxxKe2+/FlNNRKdp7Jq8PGy2q
dAJxFXfcpM+TqcOtF4UIfPcZ6rDm/9McJ9uUj4nNKPilgZhsxeRGwaXJVFgaHB+h
+BYGWRQm8WBXBj4aMyyf2/T4mV/PeSqXY+2N28rEUjhw2E3rMCF4gHiauVv8qn19
hWEnKwk8IF1hJLkbtXLhF41KPCVC++x7EtQ27t1RE5+KAlH4bm3+CRdHqkjAyzCt
90m+3iTB5ThUk9mBZk3Ozr1+w7vvjUlyUSe0/hC5MLc1B7ee/bu2JBBRCcgXziBt
YHqFCRKQz3nc1vQXiYDhUgjZRq74mkzFN6H+X+Y9Sk3VUHJazvsGmmwEy1Cdw6kf
dgmH+hE/hlRBdk6licZic0kCAwEAAQ==
-----END PUBLIC KEY-----";

// Verify the JWT access token using the public key.
// If it is not valid, remove all cookies and redirect to /login.php
try {
    $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
    $roles = $decoded->authorization->$tenantId->roles;
    if (!in_array('admin', $roles)) {
        throw new Exception('Unauthorized');
    }
} catch (Exception $e) {
    setcookie("access_" . $tenantId, "", time() - 3600);
    setcookie("id_" . $tenantId, "", time() - 3600);
    header("Location: /protected.php");
    die();
}
?>

<html>
<head>
    <meta charset="utf-8">
    <title>Admin dashboard</title>
    <script id="Userfront-script">
        (function(m,o,d,u,l,a,r,i,z,e) {
            u[m]={rq:[],ready:function(j){u[m].rq.push(j);},m:m,o:o,d:d,r:r};function j(s){return encodeURIComponent(btoa(s));}z=l.getElementById(m+"-"+a);r=u.location;
            e=[d+"/page/"+o+"/"+j(r.pathname)+"/"+j(r.host)+"?t="+Date.now(),d];e.map(function(w){i=l.createElement(a);i.defer=1;i.src=w;z.parentNode.insertBefore(i,z);});u.amvartem=m;
        })("Userfront", "pn4qwpby", "https://cdn.userfront.com/toolkit",window,document,"script");
    </script>
</head>
<body>

<a href="/">Home</a> / Admin only
<br>
<br>

This page is for admins only. This user has the following roles:
<?php
echo print_r(implode(', ',(array) $roles), true);
?>

<br>
<br>
<button onclick="Userfront.logout()">Logout</button>

</body>
</html>

# Preview

Logged in admin page:

Here is the an additional check to make sure the JWT access token has the admin role:

<!-- From /admin.php -->

try {
    $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
    $roles = $decoded->authorization->$tenantId->roles;
    if (!in_array('admin', $roles)) {
        throw new Exception('Unauthorized');
    }
} catch (Exception $e) {
    setcookie("access_" . $tenantId, "", time() - 3600);
    setcookie("id_" . $tenantId, "", time() - 3600);
    header("Location: /protected.php");
    die();
}

JWT access token payload

The JWT access token has an authorization object that contains the user's roles:

{
    "userId": 1,
    "tenantId": "pn4qwpby",
    "authorization": {
        "pn4qwpby": {
            "roles": ["admin"]
        }
    }
}

# Sample code

The above routes are combined into a working sample below, where you can navigate a website running the live code.

The website is hosted at https://oaqaa.ciroue.com/ (opens new window) and may require you to visit the PHP sandbox editor (opens new window) first before the website works.

Last Updated: 8/13/2021, 9:53:58 PM