Source: Services/Auth.php

<?php

namespace WCPOS\WooCommercePOS\Services;

use Exception;
use WCPOS\Vendor\Firebase\JWT\JWT;
use WCPOS\Vendor\Firebase\JWT\Key;
use Ramsey\Uuid\Uuid;
use WP_Error;
use WP_User;
use const DAY_IN_SECONDS;

/**
 * Auth Service class
 */
class Auth {
	/**
	 * The single instance of the class.
	 *
	 * @var Auth|null
	 */
	private static $instance = null;

		/**
		 * Gets the singleton instance.
		 *
		 * @return Auth
		 */
	public static function instance(): Auth {
		if ( null === self::$instance ) {
				self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor is private to prevent direct instantiation.
	 * Or Auth::instance() instead.
	 */
	public function __construct() {
	}

	/**
	 * Generate a secret key if it doesn't exist, or return the existing one
	 *
	 * @return string
	 */
	public function get_secret_key(): string {
		$secret_key = get_option( 'woocommerce_pos_secret_key' );
		if ( false === $secret_key || empty( $secret_key ) ) {
			$secret_key = wp_generate_password( 64, true, true );
			update_option( 'woocommerce_pos_secret_key', $secret_key );
		}
		return $secret_key;
	}

	/**
	 * Validate the provided JWT token
	 *
	 * @param string $token
	 *
	 * @return object|WP_Error
	 */
	public function validate_token( $token = '' ) {
		try {
			$decoded_token = JWT::decode( $token, new Key( $this->get_secret_key(), 'HS256' ) );

			// The Token is decoded now validate the iss
			if ( get_bloginfo( 'url' ) != $decoded_token->iss ) {
				// The iss do not match, return error
				return new WP_Error(
					'woocommmerce_pos_auth_bad_iss',
					'The iss do not match with this server',
					array( 'status' => 403 )
				);
			}

			// So far so good, validate the user id in the token
			if ( ! isset( $decoded_token->data->user->id ) ) {
				// No user id in the token, abort!!
				return new WP_Error(
					'woocommmerce_pos_auth_bad_request',
					'User ID not found in the token',
					array(
						'status' => 403,
					)
				);
			}

			/** Everything looks good return the decoded token */
			return $decoded_token;
		} catch ( Exception $e ) {
				// Something is wrong trying to decode the token, send back the error
			return new WP_Error(
				'woocommmerce_pos_auth_invalid_token',
				$e->getMessage(),
				array(
					'status' => 403,
				)
			);
		}
	}

	/**
	 * Generate a JWT token for the provided user
	 *
	 * @param WP_User $user
	 *
	 * @return string|WP_Error
	 */
	/**
	 * Generate a JWT token for the provided user
	 *
	 * @param WP_User $user
	 *
	 * @return string|WP_Error
	 */
	public function generate_token( WP_User $user ) {
		// First thing, check the secret key if not exist return a error
		if ( ! $this->get_secret_key() ) {
			return new WP_Error(
				'woocommerce_pos_jwt_auth_bad_config',
				__( 'JWT is not configured properly, please contact the admin', 'woocommerce-pos' ),
				array(
					'status' => 403,
				)
			);
		}

		/** Valid credentials, the user exists create the according Token */
		$issued_at = time();

		/**
		 * Filters the JWT issued at time.
		 *
		 * @param {string} $issued_at
		 * @returns {string} Issued at time
		 * @since 1.0.0
		 * @hook woocommerce_pos_jwt_auth_not_before
		 */
		$not_before = apply_filters( 'woocommerce_pos_jwt_auth_not_before', $issued_at, $issued_at );

		/**
		 * Filters the JWT expire time.
		 *
		 * @param {string} $issued_at
		 * @returns {string} Expire time
		 * @since 1.0.0
		 * @hook woocommerce_pos_jwt_auth_expire
		 */
		$expire = apply_filters( 'woocommerce_pos_jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at );

		$token = array(
			'iss'  => get_bloginfo( 'url' ),
			'iat'  => $issued_at,
			'nbf'  => $not_before,
			'exp'  => $expire,
			'data' => array(
				'user' => array(
					'id' => $user->data->ID,
				),
			),
		);

		/**
		 * Let the user modify the token data before the sign.
		 *
		 * @param {string} $token
		 * @param {WP_User} $user
		 * @returns {string} Token
		 * @since 1.0.0
		 * @hook woocommerce_pos_jwt_auth_token_before_sign
		 */
		$token = JWT::encode( apply_filters( 'woocommerce_pos_jwt_auth_token_before_sign', $token, $user ), $this->get_secret_key(), 'HS256' );

		return $token;
	}


	/**
	 * Get user's data
	 *
	 * @param WP_User $user
	 *
	 * @return array
	 */
	public function get_user_data( WP_User $user ): array {
		$data = array(
			'uuid'         => $this->get_user_uuid( $user ),
			'id'           => $user->ID,
			'jwt'          => $this->generate_token( $user ),
			'username'     => $user->user_login,
			'email'        => $user->user_email,
			'first_name'   => $user->user_firstname,
			'last_name'    => $user->user_lastname,
			'nice_name'    => $user->user_nicename,
			'display_name' => $user->display_name,
			'avatar_url'   => get_avatar_url( $user->ID ),
		);

		return $data;
	}

	/**
	 * Note: usermeta is shared across all sites in a network, this can cause issues in the POS.
	 * We need to make sure that the user uuid is unique per site.
	 *
	 * @param WP_User $user
	 * @return string
	 */
	private function get_user_uuid( WP_User $user ): string {
		$meta_key = '_woocommerce_pos_uuid';

		if ( function_exists( 'is_multisite' ) && is_multisite() ) {
			$meta_key = $meta_key . '_' . get_current_blog_id();
		}

		$uuid = get_user_meta( $user->ID, $meta_key, true );
		if ( ! $uuid ) {
			$uuid = Uuid::uuid4()->toString();
			update_user_meta( $user->ID, $meta_key, $uuid );
		}

		return $uuid;
	}

	/**
	 * Revoke JWT Token
	 */
	public function revoke_token(): void {
		// Implementation
	}

	/**
	 * Refresh JWT Token.
	 */
	public function refresh_token(): void {
		// Implementation
	}
}