i finally committed i guess
Signed-off-by: boris <boris@borishub.co.uk>
This commit is contained in:
199
Models/AuthService.php
Normal file
199
Models/AuthService.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
require_once('UserDataSet.php');
|
||||
|
||||
/**
|
||||
* Authentication service for handling JWT-based authentication
|
||||
*/
|
||||
class AuthService {
|
||||
private string $secretKey;
|
||||
private int $tokenExpiry;
|
||||
|
||||
/**
|
||||
* Initialises the authentication service
|
||||
* Loads configuration from environment variables
|
||||
* @throws Exception if OpenSSL extension is not loaded
|
||||
*/
|
||||
public function __construct() {
|
||||
// Load environment variables from .env file
|
||||
$envFile = __DIR__ . '/../.env';
|
||||
if (file_exists($envFile)) {
|
||||
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
// Skip comments
|
||||
if (strpos($line, '#') === 0) continue;
|
||||
|
||||
// Parse environment variable
|
||||
list($name, $value) = explode('=', $line, 2);
|
||||
$name = trim($name);
|
||||
$value = trim($value);
|
||||
|
||||
if (!empty($name)) {
|
||||
putenv(sprintf('%s=%s', $name, $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set configuration from environment variables with defaults
|
||||
$this->secretKey = getenv('JWT_SECRET_KEY') ?: 'your-256-bit-secret';
|
||||
$this->tokenExpiry = (int)(getenv('JWT_TOKEN_EXPIRY') ?: 3600);
|
||||
|
||||
// Verify OpenSSL extension is available
|
||||
if (!extension_loaded('openssl')) {
|
||||
throw new Exception('OpenSSL extension is required for JWT');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a JWT token for a user
|
||||
* @param array $userData User information to include in token
|
||||
* @return string The generated JWT token
|
||||
*/
|
||||
public function generateToken(array $userData): string {
|
||||
$issuedAt = time();
|
||||
$expire = $issuedAt + $this->tokenExpiry;
|
||||
|
||||
$payload = [
|
||||
'iat' => $issuedAt,
|
||||
'exp' => $expire,
|
||||
'uid' => $userData['id'],
|
||||
'username' => $userData['username'],
|
||||
'accessLevel' => $userData['userType']
|
||||
];
|
||||
|
||||
return $this->encodeJWT($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a JWT token
|
||||
* @param string $token The JWT token to validate
|
||||
* @return array|null The decoded payload if valid, null otherwise
|
||||
*/
|
||||
public function validateToken(string $token): ?array {
|
||||
try {
|
||||
$payload = $this->decodeJWT($token);
|
||||
|
||||
// Check if token is expired
|
||||
if ($payload === null || !isset($payload['exp']) || $payload['exp'] < time()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes data into a JWT token
|
||||
* @param array $payload The data to encode
|
||||
* @return string The encoded JWT token
|
||||
*/
|
||||
private function encodeJWT(array $payload): string {
|
||||
// Create and encode header
|
||||
$header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
|
||||
$header = $this->base64UrlEncode($header);
|
||||
|
||||
// Create and encode payload
|
||||
$payload = json_encode($payload);
|
||||
$payload = $this->base64UrlEncode($payload);
|
||||
|
||||
// Create and encode signature
|
||||
$signature = hash_hmac('sha256', "$header.$payload", $this->secretKey, true);
|
||||
$signature = $this->base64UrlEncode($signature);
|
||||
|
||||
return "$header.$payload.$signature";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a JWT token
|
||||
* @param string $token The JWT token to decode
|
||||
* @return array|null The decoded payload if valid, null otherwise
|
||||
*/
|
||||
private function decodeJWT(string $token): ?array {
|
||||
// Split token into components
|
||||
$parts = explode('.', $token);
|
||||
if (count($parts) !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$header, $payload, $signature] = $parts;
|
||||
|
||||
// Verify signature
|
||||
$validSignature = $this->base64UrlEncode(
|
||||
hash_hmac('sha256', "$header.$payload", $this->secretKey, true)
|
||||
);
|
||||
|
||||
if ($signature !== $validSignature) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Decode and return payload
|
||||
return json_decode($this->base64UrlDecode($payload), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes data using base64url encoding
|
||||
* @param string $data The data to encode
|
||||
* @return string The encoded data
|
||||
*/
|
||||
private function base64UrlEncode(string $data): string {
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes base64url encoded data
|
||||
* @param string $data The data to decode
|
||||
* @return string The decoded data
|
||||
*/
|
||||
private function base64UrlDecode(string $data): string {
|
||||
return base64_decode(strtr($data, '-_', '+/') . str_repeat('=', 3 - (3 + strlen($data)) % 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a refresh token for a user
|
||||
* @param array $userData User information to include in token
|
||||
* @return string The generated refresh token
|
||||
*/
|
||||
public function generateRefreshToken(array $userData): string {
|
||||
$issuedAt = time();
|
||||
$expire = $issuedAt + ($this->tokenExpiry * 24); // Refresh token lasts 24 times longer than access token
|
||||
|
||||
$payload = [
|
||||
'iat' => $issuedAt,
|
||||
'exp' => $expire,
|
||||
'uid' => $userData['id'],
|
||||
'username' => $userData['username'],
|
||||
'type' => 'refresh'
|
||||
];
|
||||
|
||||
return $this->encodeJWT($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes an access token using a refresh token
|
||||
* @param string $refreshToken The refresh token
|
||||
* @return string|null The new access token if valid, null otherwise
|
||||
*/
|
||||
public function refreshToken(string $refreshToken): ?string {
|
||||
try {
|
||||
$payload = $this->decodeJWT($refreshToken);
|
||||
|
||||
// Check if token is expired or not a refresh token
|
||||
if ($payload === null || !isset($payload['exp']) || $payload['exp'] < time() ||
|
||||
!isset($payload['type']) || $payload['type'] !== 'refresh') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generate a new access token
|
||||
$userData = [
|
||||
'id' => $payload['uid'],
|
||||
'username' => $payload['username'],
|
||||
'userType' => isset($payload['accessLevel']) ? $payload['accessLevel'] : 0
|
||||
];
|
||||
|
||||
return $this->generateToken($userData);
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user