Milk Admin

Token Class

When you are a logged-in user, all POST data submissions both through forms and fetch are automatically protected against CSRF attacks, so there's no need to do anything. However, to protect a non-logged form submission you need to manually protect the submission by generating a token and verifying it on the PHP side.

The Token class is used to generate and verify protection tokens. It handles two main types of tokens: those for protection against CSRF attacks and those for API authentication through JWT.

CSRF Protection

Cross-Site Request Forgery attacks are attacks where a malicious actor induces an authenticated user to perform unwanted actions on a web application, exploiting their active authentication to send unauthorized requests to the server without the user noticing.

JWT Authentication

The JWT (JSON Web Token) system authenticates API calls through a simple and effective process. Initially, the user's credentials are sent to the server, which generates a JWT token containing the necessary information and sends it to the user. From this moment, the user includes the token in every subsequent request, allowing the server to verify identity and permissions. When the token expires, it's possible to generate a new token without having to resend authentication credentials.

CSRF Token Functions

config($secret_key, $token_key)

Configures the secret key and token key for CSRF protection.

Token::config('secret_key', 'token_key');

input($name)

Generates a hidden HTML input containing the CSRF token.

echo Token::input('form_name');

Through the input it's possible to protect a form submission from CSRF attacks.

<form action="..." method="post"><?php echo Token::input('form_name'); ?></form>

In this case you use check to verify the token. Check also finds the input where the token is passed.

if (Token::check('form_name')) { //... }

get($name)

Generates a CSRF token for browser call authentication.

$token = Token::get('form_name');

This is useful if we are protecting an ajax call since input generates an input whose name is random

PHP

$token = Token::get('form_name');

Javascript

fetch(milk_url, {
    method: 'POST',
    credentials: 'same-origin',
    body: formData
}).then((response) => {
    return response.json();
}).then((data) => {
   console.log(data);
});

PHP-side verification

if (Token::checkValue($_POST['token'], 'form_name')) { //... }

getTokenKey($name)

Returns the token key generated from the provided name.

$tokenKey = Token::getTokenKey('form_name');

getTokenName($name)

Returns the name of the variable where the token is stored.

$tokenName = Token::getTokenName('form_name');

check($name)

Verifies if the CSRF token is correct, also finding the variable name.

$isValid = Token::check('form_name');

checkValue($token, $name)

Verifies if the CSRF token value is correct.

$isValid = Token::checkValue($token, 'form_name');

JWT Token Functions

configJwt($private_key, $public_key, $expiration)

Configures keys and expiration for the JWT system. If parameters are not provided, global configurations are used.

Token::configJwt($private_key, $public_key, 3600); // 1 hour

generateKeyPair()

Generates a public/private key pair for JWT authentication with 2048-bit RSA algorithm.

$keys = Token::generateKeyPair();
if ($keys) {
    $private_key = $keys['private_key'];
    $public_key = $keys['public_key'];
}

generateJwt($user_id, $additional_data)

Generates a JWT token for a specific user. It's possible to add custom data to the token payload.

// Base token with only user_id
$token = Token::generateJwt(123);

// Token with additional data
$additional_data = [
    'role' => 'admin',
    'permissions' => ['read', 'write', 'delete']
];
$token = Token::generateJwt(123, $additional_data);

verifyJwt($token)

Verifies a JWT token and returns its payload if valid. Automatically checks expiration and token integrity.

$payload = Token::verifyJwt($token);
if ($payload) {
    $user_id = $payload['user_id'];
    $role = $payload['role'] ?? null;
    echo "Authenticated user: " . $user_id;
} else {
    echo "Invalid token: " . Token::$last_error;
}

Property $last_error

Contains the last error that occurred during token operations. Useful for debugging and error handling.

if (!Token::check('form_name')) {
    echo "Error: " . Token::$last_error;
}

Complete JWT Usage Example

Complete API authentication example using JWT tokens:

// Initial configuration
Token::configJwt($private_key, $public_key, 7200); // 2 hours

// Token generation after login
$user_id = 123;
$user_data = [
    'email' => 'user@example.com',
    'role' => 'user'
];
$jwt_token = Token::generateJwt($user_id, $user_data);

// Send token to client
header('Content-Type: application/json');
echo json_encode(['token' => $jwt_token]);

// Token verification in subsequent requests
$auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (preg_match('/Bearer\s+(.*)$/i', $auth_header, $matches)) {
    $token = $matches[1];
    $payload = Token::verifyJwt($token);
    
    if ($payload) {
        // Valid token, proceed with operation
        $current_user_id = $payload['user_id'];
        $user_email = $payload['email'];
    } else {
        // Invalid token
        http_response_code(401);
        echo json_encode(['error' => 'Invalid token']);
    }
}
Loading...