Token Lifecycle Management
Token Creation and Storage
use Magento\Integration\Model\TokenFactory;
use Magento\Framework\Encryption\EncryptorInterface;
class TokenManager
{
private $tokenFactory;
private $encryptor;
public function __construct(
TokenFactory $tokenFactory,
EncryptorInterface $encryptor
) {
$this->tokenFactory = $tokenFactory;
$this->encryptor = $encryptor;
}
public function createToken($consumerId, $accessToken, $secret)
{
$token = $this->tokenFactory->create();
$token->setConsumerId($consumerId);
$token->setAccessToken($this->encryptor->hash($accessToken));
$token->setSecret($this->encryptor->hash($secret));
$token->setCreatedAt(gmdate('Y-m-d H:i:s'));
$token->save();
return $token;
}
public function revokeToken($tokenId)
{
$token = $this->tokenFactory->create()->load($tokenId);
$token->delete();
}
}
Token Expiration
// Check token expiration
public function isTokenExpired($token)
{
$createdAt = strtotime($token->getCreatedAt());
$lifetime = $this->config->getTokenLifetime();
return (time() - $createdAt) > $lifetime;
}
// Token refresh flow
public function refreshToken($refreshToken)
{
$existingToken = $this->validateRefreshToken($refreshToken);
if (!$existingToken || $this->isTokenExpired($existingToken)) {
throw new \Exception('Invalid or expired refresh token');
}
$newToken = $this->generateAccessToken();
$existingToken->setAccessToken($this->encryptor->hash($newToken));
$existingToken->save();
return $newToken;
}
Key Points
- Store tokens encrypted at rest
- Implement token expiration (4 hours for admin tokens)
- Support token revocation for security incidents
- Use refresh tokens for long-lived access
Rate Limiting Configuration
Magento Rate Limiting
use Magento\Framework\App\Response\HttpInterface;
use Magento\Framework\Cache\FrontendInterface;
class RateLimiter
{
private $cache;
private $config;
public function __construct(
FrontendInterface $cache,
ConfigInterface $config
) {
$this->cache = $cache;
$this->config = $config;
}
public function checkRateLimit($identifier, $maxRequests, $windowSeconds)
{
$cacheKey = 'rate_limit_' . $identifier;
$data = $this->cache->load($cacheKey);
if ($data) {
$requests = unserialize($data);
$windowStart = time() - $windowSeconds;
// Remove old requests
$requests = array_filter($requests, function($timestamp) use ($windowStart) {
return $timestamp > $windowStart;
});
} else {
$requests = [];
}
if (count($requests) >= $maxRequests) {
return false; // Rate limit exceeded
}
$requests[] = time();
$this->cache->save(serialize($requests), $cacheKey, [], $windowSeconds);
return true;
}
}
Rate Limit Headers
public function addRateLimitHeaders($response, $limit, $remaining, $reset)
{
$response->setHeader('X-RateLimit-Limit', $limit);
$response->setHeader('X-RateLimit-Remaining', $remaining);
$response->setHeader('X-RateLimit-Reset', $reset);
if ($remaining <= 0) {
$response->setHeader('Retry-After', $reset - time());
}
}
Key Points
- Rate limit by IP address or API key
- Return 429 Too Many Requests when exceeded
- Include rate limit headers in responses
- Consider different limits for different endpoints
CORS Configuration
Magento CORS Setup
use Magento\Framework\App\Response\HeaderProviderInterface;
class CorsHeaders implements HeaderProviderInterface
{
private $config;
public function __construct(ConfigInterface $config)
{
$this->config = $config;
}
public function getHeaders()
{
$allowedOrigins = $this->config->getAllowedOrigins();
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins)) {
return [
'Access-Control-Allow-Origin' => $origin,
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => '86400',
];
}
return [];
}
}
GraphQL CORS
// graphql.xml configuration
<config>
<router id="standard">
<route url="/graphql" frontName="graphql" />
</router>
<!-- CORS for GraphQL endpoint -->
<cors>
<allowed_origins>
<origin>https://store.example.com</origin>
<origin>https://admin.example.com</origin>
</allowed_origins>
</cors>
</config>
Key Points
- Never use Access-Control-Allow-Origin: * in production
- Validate Origin header before allowing access
- Handle preflight OPTIONS requests
- Limit allowed methods and headers
OAuth 2.0 Implementation
OAuth 2.0 Flow
// Authorization Code Grant
public function authorize($clientId, $redirectUri, $scope)
{
$client = $this->validateClient($clientId, $redirectUri);
if (!$client) {
throw new \Exception('Invalid client');
}
$code = $this->generateAuthorizationCode($client, $scope);
return $this->redirect($redirectUri . '?code=' . $code);
}
public function token($grantType, $code, $clientId, $clientSecret)
{
if ($grantType !== 'authorization_code') {
throw new \Exception('Unsupported grant type');
}
$client = $this->validateClient($clientId, $clientSecret);
$authCode = $this->validateAuthorizationCode($code, $client);
$accessToken = $this->generateAccessToken($client, $authCode['scope']);
$refreshToken = $this->generateRefreshToken($client);
return [
'access_token' => $accessToken,
'token_type' => 'Bearer',
'expires_in' => 3600,
'refresh_token' => $refreshToken,
];
}
Scopes and Permissions
// Define API scopes
$scopes = [
'catalog' => ['read', 'write'],
'orders' => ['read', 'write'],
'customers' => ['read'],
'inventory' => ['read', 'write'],
];
// Validate scope
public function hasScope($token, $requiredScope)
{
$tokenScopes = explode(' ', $token->getScope());
return in_array($requiredScope, $tokenScopes);
}
Key Points
- Use HTTPS for all OAuth endpoints
- Validate client credentials strictly
- Implement short-lived access tokens
- Support refresh token rotation
Practice Problems
0 / 1 solved
Rate Limiter Implementation
Implement a rate limiter that tracks requests per API key and returns proper headers.
Solution
class RateLimiter {
private $cache;
public function check($apiKey, $limit, $window) {
$key = 'rl_' . $apiKey;
$data = $this->cache->load($key);
$requests = $data ? unserialize($data) : [];
$windowStart = time() - $window;
$requests = array_filter($requests, fn($t) => $t > $windowStart);
if (count($requests) >= $limit) {
return false;
}
$requests[] = time();
$this->cache->save(serialize($requests), $key, [], $window);
return true;
}
public function getHeaders($apiKey) {
$remaining = $this->getRemainingRequests($apiKey);
return [
'X-RateLimit-Limit' => 100,
'X-RateLimit-Remaining' => $remaining,
];
}
} Quiz
1. What HTTP status code indicates rate limit exceeded?
2. Why not use Access-Control-Allow-Origin: *?
3. What is the recommended admin token lifetime?
4. How should API secrets be stored?
Flashcards
Question
Rate limit status code?
Click to reveal answer
Answer
429 Too Many Requests
Question
CORS wildcard risk?
Click to reveal answer
Answer
Allows any origin to access API, security vulnerability
Question
Token storage best practice?
Click to reveal answer
Answer
Encrypt at rest, store in environment variables
Question
OAuth 2.0 grant types?
Click to reveal answer
Answer
Authorization Code, Client Credentials, Refresh Token
Revision Notes
Key Takeaways
- 1. Implement token expiration and revocation
- 2. Rate limit by IP or API key with proper headers
- 3. CORS must validate specific origins, not wildcards
- 4. OAuth 2.0 provides secure delegated access
Interview Tips
- • Explain token lifecycle management
- • Discuss rate limiting strategies
- • Know OAuth 2.0 flows and security considerations
Cheat Sheet
API Security
- Tokens: encrypted, expired, revocable
- Rate limit: 429 + headers
- CORS: specific origins only
- OAuth: authorization code flow
- Always use HTTPS