Authentication

Authentication is a crucial aspect of web applications, ensuring that users are who they claim to be and have the necessary permissions to access resources. Prisma PHP simplifies the authentication process by providing a built-in authentication system that supports various authentication methods, including email/password, social logins, and third-party services. By integrating authentication into the development workflow, Prisma PHP streamlines the process of securing applications, making it easier for developers to implement user authentication and authorization features.

By default, Prisma PHP use the package firebase/php-jwt for authentication. This package is a PHP library that enables developers to encode and decode JSON Web Tokens (JWTs) in PHP. JWTs are a popular method for securely transmitting information between parties as a JSON object. They are commonly used for authentication and authorization in web applications, providing a secure and efficient way to verify the identity of users and grant access to resources. For more information about JWTs, you can visit the official website: jwt.io.

To get stated with authentication you have to follow the following steps:

  1. Set the AUTH_SECRET environment variable in the .env file.
  2. Use the Auth class to authenticate users and generate JWTs, location src/Lib/Auth/Auth.php.
  3. Use the AuthConfig class to configure the authentication settings, location src/Lib/Auth/AuthConfig.php.
  4. Implement authentication middleware AuthMiddleware to protect routes and resources, location src/Lib/Middleware/AuthMiddleware.php.
  5. Secure your application by validating JWTs and verifying user roles.

Set the AUTH_SECRET Environment Variable

The AUTH_SECRET environment variable is used to sign and verify JWTs. It should be a random string of characters that is kept secret and not shared with others. You can generate a secure secret using a password manager or a random string generator. The secret should be unique for each application and should not be hard-coded in the source code.

By default Prisma PHP give you a random secret key AUTH_SECRET=your_secret_key just for development purposes, you can change it in the .env file.

  • Get your secret key from openssl CLI, openssl rand -base64 33
  • Alternatively, randomkeygen.com.
  • Or install the package on local running the command npm exec auth secret, this will generate a random secret key and save it in the .env file.

Use the Auth Class

To authenticate users and generate JWT tokens, you can use the Auth singleton class located in src/Lib/Auth/Auth.php. This class provides all the necessary methods for authentication, such as:

  • getInstance Get the instance of the Auth class, $auth = Auth::getInstance();
  • signIn Sign In the user and generate a JWT token, $jwt = $auth->signIn($data);
  • isAuthenticated Check if the user is authenticated, $auth->isAuthenticated();
  • getPayload Get the payload of the JWT token. Use the $user = $auth->getPayload() method to retrieve the information encrypted in the JWT token. This method will return the user information that was saved while generating the token, but decrypted.
  • verifyToken Verify the JWT token, $auth->verifyToken($token);
  • refreshToken Refresh the JWT token, $auth->refreshToken($token);
  • signOut Sign Out the user and destroy the JWT token, $auth->signOut('/redirect');

Use the AuthConfig Class

To configure the AuthConfig class you can find it in src/Lib/Auth/AuthConfig.php file, you can change the default values as you want.

  • ROLE_IDENTIFIER: The key used to identify the user role in the JWT payload.
  • IS_ROLE_BASE: A boolean value indicating whether the application uses role-based access control.
  • IS_TOKEN_AUTO_REFRESH: A boolean value indicating whether the JWT token should be automatically refreshed.
  • public static $publicRoutes: An array listing the public routes that do not require authentication, by default all routes is public.
  • public static $privateRoutes: An array of private routes that are accessible to all authenticated users without specific role-based access control.
  • public static $roleBasedRoutes: An associative array mapping specific routes to required user roles for access control.

Here is an example of how to configure the role base access control in the AuthConfig class:

if you going to use the role base access control you have to set the IS_ROLE_BASE to true and set the roles for each route in the public static $privateRoutes and public static $roleBasedRoutes array. The roles should be defined in the AuthRole enum, you can find it in the src/Lib/Auth/AuthConfig.php file.

enum AuthRole: string
  {
      case Admin = 'Admin';
      case User = 'User';
  
      public function equals($role)
      {
          return $this->value === $role;
      }
  }

Here is an example of how to configure the AuthConfig class:

final class AuthConfig
    {
        const ROLE_IDENTIFIER = 'role'; // The key used to identify the user role in the JWT payload
        const IS_ROLE_BASE = false; // Enable role-based access control (true/false)
        const IS_TOKEN_AUTO_REFRESH = false; // Enable token auto refresh (true/false)
        // Be default all routes is public
        public static $privateRoutes = [
            '/dashboard', // All routes that is inside the dashboard route is private and require authentication
            '/settings/profile', // All routes that is inside the settings/profile route is private and require authentication 
            **Note** the settings route is public
        ];
        public static $roleBasedRoutes = [
            'dashboard' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]], // The dashboard route require Admin or User role to access
            'dashboard/users' => [self::ROLE_IDENTIFIER => [AuthRole::Admin]], // The dashboard/users route require Admin role to access 
            **Note** the dashboard route is accessible by Admin and User roles
        ];
    }

After configuring the AuthConfig class, you can use the Auth class to authenticate users and generate JWTs. The Auth class provides methods for authenticating users and generating JWTs. You can use the Auth->authenticate($role, $tokenValidity) method to authenticate users and generate a JWT token. The method takes a mixed $role and a string $tokenValidity as arguments and returns a signed JWT token. You can use the token to authenticate requests and grant access to protected resources.

You can modify the authenticate method to accept the user ID and token validity as arguments, this makes the method more flexible and allows you to specify the $role can be the user model that you want to store in the JWT payload.

  • For the token validity $tokenValidity, you can use the following formats:
    • 30s for 30 seconds
    • 1m for 1 minute
    • 1h for 1 hour
    • 1d for 1 day
  • By default, the token validity is set to 1h for 1 hour. If you don't specify the token validity, it will use the default value.

Here is an example of how to use the Auth class to authenticate users and generate JWTs:

<?php
  
  use Lib\Auth\Auth;
  
  $auth = Auth::getInstance();
  $loginUserInfo = ['id' => 1, 'username' => 'john.doe', 'email' => 'john.doe@gmail.com'];
  try {
      $jwt = $auth->authenticate($loginUserInfo);
      echo "JWT: " . $jwt;
  } catch (\InvalidArgumentException $e) {
      echo "Error: " . $e->getMessage();
  }

Note: The basic way to authenticate the user is by passing the user info directly to the `authenticate` method. In this case, there is no need to handle the `Role` separately.

Basic way to authenticate the user or login the user is by passing the user info directly to the `authenticate` method, in this case, there is no need to handle the `Role` separately.

<?php

  use Lib\Auth\Auth;
  use Lib\StateManager;
  
  $auth = Auth::getInstance();
  
  $user = StateManager::getState('user');
  
  if ($auth->isAuthenticated()) {
      StateManager::setState('user', $auth->getPayload());
  }
  
  function login()
  {
      global $auth;
  
      $jwt = $auth->authenticate(['name' => 'admin'], '1m');
      echo "JWT: $jwt";
  }
  
  ?>
  
  <button onclick="login">Login</button>
  <p><?= $user->name ?? '' ?></p>
<?php
  
  use Lib\Auth\Auth;
  use Lib\Auth\AuthRole;
  
  $auth = Auth::getInstance();
  
  try {
      $jwt = $auth->authenticate(AuthRole::Admin, '1m');
      echo "JWT: " . $jwt;
  } catch (\InvalidArgumentException $e) {
      echo "Error: " . $e->getMessage();
  }

In this example, we create a new instance of the Auth class from the Lib\Auth namespace. We then attempt to authenticate a user as an Admin with a token expiration time of '1m' (one minute). The authenticate method generates a JWT (JSON Web Token) which is then printed out. If an error occurs during authentication, such as invalid arguments, it catches the exception and prints the error message.

To check is the user is authenticated you can use the Auth->isAuthenticated() method, this method will return true if the user is authenticated and false if the user is not authenticated.

For logout you can use the Auth->logout() method to logout the user and destroy the JWT token, this method will redirect the user to the login page.

Here is an example of how to use the Auth class to logout the user:

To log out the user, you can use the signOut function and then redirect the user to the home page.

<?php

  use Lib\Auth\Auth;
  
  function logout {
    Auth::getInstance()->signOut('/'); // Replace '/path/to/redirect' with the actual path where you want to redirect the user after logout
  }

  ?>

  <button onclick="logout">Logout</button>

using get to logout the user, <a href="?logout">Logout</a>

<?php

  use Lib\Auth\Auth;
  
  $auth = Auth::getInstance();
  
  if ($isGet && isset($params->logout)) {
      $auth->logout('/'); // Replace '/path/to/redirect' with the actual path where you want to redirect the user after logout
  }
  
  ?>
  
  <a href="?logout">Logout</a>

Implement Authentication Middleware

Once you have generated a JWT token, you can use it to authenticate requests and grant access to protected resources. To do this, you need to implement authentication middleware that verifies the JWT token and grants access to authenticated users. The middleware should extract the JWT token from the request headers, decode the token, and verify the signature to ensure that it is valid. If the token is valid, the middleware should grant access to the protected resource; otherwise, it should return an error response or redirect.

Prisma PHP provides built-in middleware for authenticating requests and protecting routes. You can use the AuthMiddleware class to authenticate requests and grant access to protected resources. The middleware extracts the JWT token from the request headers, decodes the token, and verifies the signature to ensure that it is valid. If the token is valid, the middleware grants access to the protected resource; otherwise, it returns an error response or redirect.

Here is an example of how to use the AuthMiddleware class to authenticate requests and protect routes:

public static function handle($requestUri)
  {
      // Check if the route matches the private routes that require authentication
      $requestUri = trim($requestUri);
      if (!self::matches($requestUri)) {
          return;
      }
      // Check if the user is authorized to access the route or redirect to login
      if (!self::isAuthorized()) {
          redirect('/auth/login'); // Redirect to login page or return an error response depends on your implementation
          exit;
      }
      // Check if the user has the required role to access the route or redirect to denied
      if (AuthConfig::IS_ROLE_BASE && !self::hasRequiredRole($requestUri)) {
          redirect('/denied'); // Redirect to denied page or return an error response depends on your implementation
          exit;
      }
  }

In this example, the AuthMiddleware class is used to authenticate requests and protect routes. It first checks if the requested URI matches a list of private routes that require authentication. If the URI does not match, it simply returns without further checks. If it matches, the middleware verifies whether the user is authorized. If not authorized, it redirects the user to a login page. Additionally, if role-based access control is enabled and the user does not have the required role for the route, it redirects to a denial page. This setup ensures that only authorized and properly role-equipped users can access specific routes.

Secure Your Application

After you've put the authentication middleware in place, it's time to leverage it to safeguard your application and its routes and resources. This middleware does the critical work of validating JWT tokens, thus ensuring only authenticated users who also meet the role-based permissions can access certain areas of your application. The configuration for this, including specifying which routes are protected and the roles that are permitted access, is handled through the AuthConfig class.

This approach of integrating authentication and authorization mechanisms into your application is instrumental in protecting sensitive information and resources from unauthorized access. It guarantees that only users with the right permissions can reach protected resources. Prisma PHP facilitates this with its in-built authentication system, significantly simplifying the implementation of these crucial security features for developers.

For further details on implementing authentication and authorization with Prisma PHP, refer to the official documentation and API guide. These resources offer comprehensive guidance on authenticating users, generating JWT tokens, and securing routes and resources with the framework's authentication capabilities.