| sidebar_position | 9 |
|---|---|
| title | JWT Tokens |
The library provides built-in support for JWT (JSON Web Token) authentication through integration with byjg/jwt-wrapper.
JWT (JSON Web Tokens) is a compact, URL-safe means of representing claims to be transferred between two parties. JWTs are commonly used for:
- Stateless authentication - No server-side session storage needed
- API authentication - Perfect for REST APIs and microservices
- Single Sign-On (SSO) - Share authentication across domains
- Mobile apps - Efficient token-based authentication
<?php
use ByJG\JwtWrapper\JwtHashHmacSecret;
use ByJG\JwtWrapper\JwtWrapper;
$secret = getenv('JWT_SECRET') ?: JwtWrapper::generateSecret(64); // base64 encoded value
$jwtKey = new JwtHashHmacSecret($secret);
$jwtWrapper = new JwtWrapper('api.example.com', $jwtKey);Use your API hostname (or any issuer string you want to validate) as the first argument to JwtWrapper.
:::danger Secret Key Security
- Keep your secret key confidential and secure
- Use a strong, random secret key (minimum 256 bits recommended)
- Store the key in environment variables, not in code
- Rotate keys periodically :::
<?php
$userToken = $users->createAuthToken(
'johndoe', // Login (username or email)
'password123', // Password
$jwtWrapper, // JWT wrapper instance
3600 // Expires in 1 hour (seconds)
);
if ($userToken !== null) {
// Return token to client
echo json_encode(['token' => $userToken->token]);
} else {
// Authentication failed
http_response_code(401);
echo json_encode(['error' => 'Invalid credentials']);
}You can include additional data in the JWT payload:
<?php
$userToken = $users->createAuthToken(
'johndoe',
'password123',
$jwtWrapper,
3600,
[], // Update user properties (optional)
[ // Additional token data
'role' => 'admin',
'permissions' => ['read', 'write'],
'tenant_id' => '12345'
]
);
// Access the token string
$token = $userToken->token;<?php
$userToken = $users->createAuthToken(
'johndoe',
'password123',
$jwtWrapper,
3600,
[ // User properties to update
'last_login' => date('Y-m-d H:i:s'),
'login_count' => $loginCount + 1
],
[ // Token data
'role' => 'admin'
]
);Instead of manually adding every field to $updateTokenInfo, pass a seventh argument with an array of User enum values or string property names. The service will read the corresponding getters (or custom properties) from the UserModel and copy them into the token payload.
<?php
use ByJG\Authenticate\Enum\UserField;
$userToken = $users->createAuthToken(
'johndoe',
'password123',
$jwtWrapper,
3600,
[],
[],
[UserField::Name, UserField::Email, 'department']
);In the example above, the token payload receives the user's name, email, and the value returned by $user->get('department') automatically.
The createInsecureAuthToken() method creates JWT tokens without validating the user's password. This is useful for:
- Creating tokens after social authentication (OAuth, SAML, etc.)
- Implementing "remember me" functionality
- Token refresh mechanisms
- Administrative token generation
:::warning Security Warning
Use createInsecureAuthToken() with caution. Only call it after you've verified the user's identity through another secure method.
:::
<?php
$userToken = $users->createInsecureAuthToken(
'johndoe', // Login (username or email)
$jwtWrapper,
3600, // Expires in 1 hour
[], // Update user properties (optional)
['auth_method' => 'oauth'] // Additional token data
);
echo "Token: " . $userToken->token;<?php
// After social authentication
$user = $users->getByEmail($oauthEmail);
if ($user !== null) {
$userToken = $users->createInsecureAuthToken(
$user, // Pass UserModel directly
$jwtWrapper,
3600,
['last_oauth_login' => date('Y-m-d H:i:s')],
['oauth_provider' => 'google']
);
echo "Token: " . $userToken->token;
}| Feature | createAuthToken | createInsecureAuthToken |
|---|---|---|
| Password validation | ✅ Required | ❌ Skipped |
| First parameter | Login string only | Login string OR UserModel |
| Use case | Normal login | OAuth, token refresh, admin operations |
| Security level | High | Use with caution |
<?php
try {
$userToken = $users->isValidToken('johndoe', $jwtWrapper, $token);
if ($userToken !== null) {
$user = $userToken->user; // UserModel instance
$tokenData = $userToken->data; // Token payload data
echo "Authenticated: " . $user->getName();
echo "Role: " . $tokenData['role'];
}
} catch (\ByJG\Authenticate\Exception\UserNotFoundException $e) {
echo "User not found";
} catch (\ByJG\Authenticate\Exception\NotAuthenticatedException $e) {
echo "Token validation failed: " . $e->getMessage();
} catch (\ByJG\JwtWrapper\JwtWrapperException $e) {
echo "JWT error: " . $e->getMessage();
}The isValidToken() method performs the following checks:
- User exists - Verifies the user account exists
- Token hash matches - Compares stored token hash
- JWT signature - Validates the token signature
- Token expiration - Checks if token has expired
When a token is created:
<?php
// A SHA-1 hash of the token is stored as a user property
$tokenHash = sha1($token);
$user->set('TOKEN_HASH', $tokenHash);This allows you to invalidate tokens without maintaining a token blacklist.
<?php
// Remove the token hash
$users->removeProperty($userId, 'TOKEN_HASH');<?php
// The next createAuthToken() call will overwrite the old hash
$newToken = $users->createAuthToken($login, $password, $jwtWrapper, 3600);<?php
// login.php
header('Content-Type: application/json');
$input = json_decode(file_get_contents('php://input'), true);
try {
$userToken = $users->createAuthToken(
$input['username'],
$input['password'],
$jwtWrapper,
3600, // 1 hour expiration
[
'last_login' => date('Y-m-d H:i:s'),
'last_ip' => $_SERVER['REMOTE_ADDR']
],
[
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT']
]
);
if ($userToken === null) {
throw new Exception('Authentication failed');
}
echo json_encode([
'success' => true,
'token' => $userToken->token,
'expires_in' => 3600
]);
} catch (Exception $e) {
http_response_code(401);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}<?php
// api.php
header('Content-Type: application/json');
// Extract token from Authorization header
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (!preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
http_response_code(401);
echo json_encode(['error' => 'No token provided']);
exit;
}
$token = $matches[1];
// Extract username from token (you need to decode it first)
try {
$jwtData = $jwtWrapper->extractData($token);
$username = $jwtData->data['login'] ?? null;
if (!$username) {
throw new Exception('Invalid token structure');
}
// Validate token
$userToken = $users->isValidToken($username, $jwtWrapper, $token);
if ($userToken === null) {
throw new Exception('Invalid token');
}
$user = $userToken->user;
// Process request
echo json_encode([
'success' => true,
'user' => [
'id' => $user->getUserid(),
'name' => $user->getName(),
'email' => $user->getEmail()
]
]);
} catch (Exception $e) {
http_response_code(401);
echo json_encode(['error' => $e->getMessage()]);
}<?php
// logout.php
header('Content-Type: application/json');
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
$token = $matches[1];
try {
$jwtData = $jwtWrapper->extractData($token);
$username = $jwtData->data['login'] ?? null;
$user = $users->getByLogin($username);
if ($user !== null) {
$users->removeProperty($user->getUserid(), 'TOKEN_HASH');
}
echo json_encode(['success' => true, 'message' => 'Logged out']);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
} else {
http_response_code(400);
echo json_encode(['error' => 'No token provided']);
}<?php
// 15 minutes
$token = $users->createAuthToken($login, $password, $jwtWrapper, 900);
// 1 hour
$token = $users->createAuthToken($login, $password, $jwtWrapper, 3600);
// 24 hours
$token = $users->createAuthToken($login, $password, $jwtWrapper, 86400);
// 7 days
$token = $users->createAuthToken($login, $password, $jwtWrapper, 604800);For long-lived sessions, implement a refresh token pattern:
<?php
// Create short-lived access token
$accessUserToken = $users->createAuthToken(
$login,
$password,
$jwtWrapper,
900, // 15 minutes
[],
['type' => 'access']
);
// Create long-lived refresh token
$refreshUserToken = $users->createAuthToken(
$login,
$password,
$jwtWrapperRefresh, // Different wrapper/key
604800, // 7 days
[],
['type' => 'refresh']
);
echo json_encode([
'access_token' => $accessUserToken->token,
'refresh_token' => $refreshUserToken->token
]);- Use HTTPS - Always transmit tokens over HTTPS
- Short expiration times - Use short-lived tokens (15-60 minutes)
- Implement refresh tokens - For longer sessions
- Validate on every request - Don't trust the client
- Store securely - Don't store tokens in localStorage if possible
- Include audience claims - Limit token usage scope
- Monitor for abuse - Track token usage patterns
- Rotate secrets - Periodically rotate JWT secrets
❌ Don't store sensitive data in JWT payload - It's not encrypted, only signed
❌ Don't use weak secret keys - Use cryptographically random keys
❌ Don't skip expiration - Always set reasonable expiration times
❌ Don't forget to invalidate - Provide logout functionality
❌ Don't use HTTP - Always use HTTPS in production
- Authentication - Other authentication methods
- Session Context - Session-based authentication
- User Properties - Managing user data