Token Yenileme (Refresh Token)
Access token'ınızın süresi dolduğunda, refresh token kullanarak yeni bir access token alabilirsiniz. Bu işlem, kullanıcının tekrar giriş yapmasına gerek kalmadan oturumun devam etmesini sağlar.
📋 Endpoint Bilgileri
- URL:
/api/v1/auth/refresh - Method:
POST - Content-Type:
application/json - Authentication: Gerekli değil (refresh token kullanılır)
🔄 Token Yenileme Süreci
sequenceDiagram
participant Client
participant API
Note over Client: Access token süresi dolmuş
Client->>API: POST /api/v1/auth/refresh
Note over Client: refreshToken gönder
API-->>Client: Yeni accessToken + refreshToken
Note over Client: Token'ları güncelle
Client->>API: API call with new token
API-->>Client: Success response
📝 Request
Headers
Content-Type: application/json
Body Parameters
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
refreshToken | string | ✅ | Daha önce alınan refresh token |
Request Example
{
"refreshToken": "def502004b8f5b592c4f6c94def50200..."
}
✅ Response
Başarılı Yanıt (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"accessTokenExpire": 1692793200,
"refreshToken": "abc123004b8f5b592c4f6c94abc12300..."
}
Response Fields
| Alan | Tip | Açıklama |
|---|---|---|
accessToken | string | Yeni API access token |
accessTokenExpire | integer | Yeni token'ın sona erme zamanı (Unix timestamp) |
refreshToken | string | Yeni refresh token (güvenlik için değişir) |
❌ Hata Yanıtları
400 Bad Request
Geçersiz refresh token format:
{
"error": "Invalid Token Format",
"message": "Refresh token formatı geçersiz",
"statusCode": 400
}
401 Unauthorized
Süresi dolmuş veya geçersiz refresh token:
{
"error": "Invalid Refresh Token",
"message": "Refresh token geçersiz veya süresi dolmuş",
"statusCode": 401
}
💻 Kod Örnekleri
JavaScript/Node.js
async function refreshAccessToken(refreshToken) {
const url = "https://integration.seoauthor.ai/api/v1/auth/refresh";
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
refreshToken: refreshToken,
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Token refresh failed: ${errorData.message}`);
}
const data = await response.json();
// Yeni token'ları sakla
sessionStorage.setItem("accessToken", data.accessToken);
sessionStorage.setItem("refreshToken", data.refreshToken);
sessionStorage.setItem("tokenExpire", data.accessTokenExpire);
console.log("Token başarıyla yenilendi");
return data;
} catch (error) {
console.error("Token yenileme hatası:", error.message);
// Refresh token da geçersizse, kullanıcıyı yeniden login'e yönlendir
if (error.message.includes("401") || error.message.includes("Invalid")) {
sessionStorage.clear();
window.location.href = "/login";
}
throw error;
}
}
// Otomatik token yenileme utility
class TokenManager {
constructor() {
this.refreshPromise = null;
}
async getValidToken() {
const accessToken = sessionStorage.getItem("accessToken");
const refreshToken = sessionStorage.getItem("refreshToken");
const tokenExpire = parseInt(sessionStorage.getItem("tokenExpire"));
if (!accessToken || !refreshToken) {
throw new Error("No tokens available");
}
// Token süresi kontrol et (5 dakika buffer)
const now = Math.floor(Date.now() / 1000);
const bufferTime = 5 * 60; // 5 dakika
if (now + bufferTime >= tokenExpire) {
// Eğer zaten bir refresh işlemi devam ediyorsa, onu bekle
if (this.refreshPromise) {
await this.refreshPromise;
return sessionStorage.getItem("accessToken");
}
// Yeni refresh işlemi başlat
this.refreshPromise = this.refreshToken(refreshToken);
try {
await this.refreshPromise;
return sessionStorage.getItem("accessToken");
} finally {
this.refreshPromise = null;
}
}
return accessToken;
}
async refreshToken(refreshToken) {
const newTokens = await refreshAccessToken(refreshToken);
return newTokens.accessToken;
}
}
// Kullanım
const tokenManager = new TokenManager();
tokenManager
.getValidToken()
.then((token) => {
console.log("Geçerli token alındı:", token.substring(0, 20) + "...");
})
.catch((error) => {
console.error("Token alınamadı:", error);
});
Python
import requests
import json
import time
from datetime import datetime
class TokenManager:
def __init__(self):
self.access_token = None
self.refresh_token = None
self.token_expire = None
def refresh_access_token(self, refresh_token):
url = "https://integration.seoauthor.ai/api/v1/auth/refresh"
headers = {
"Content-Type": "application/json"
}
data = {
"refreshToken": refresh_token
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
token_data = response.json()
# Token'ları güncelle
self.access_token = token_data['accessToken']
self.refresh_token = token_data['refreshToken']
self.token_expire = token_data['accessTokenExpire']
expire_time = datetime.fromtimestamp(self.token_expire)
print(f"Token yenilendi. Yeni sona erme zamanı: {expire_time}")
return token_data
except requests.exceptions.HTTPError as e:
if response.status_code == 401:
print("Refresh token geçersiz. Yeniden giriş gerekli.")
# Kullanıcıyı login'e yönlendir
self.clear_tokens()
raise
except requests.exceptions.RequestException as e:
print(f"Token yenileme hatası: {e}")
raise
def is_token_expired(self, buffer_seconds=300): # 5 dakika buffer
if not self.token_expire:
return True
current_time = int(time.time())
return (self.token_expire - current_time) <= buffer_seconds
def get_valid_token(self):
if not self.access_token or not self.refresh_token:
raise Exception("Token'lar mevcut değil. Giriş yapın.")
if self.is_token_expired():
print("Token süresi dolmuş, yenileniyor...")
self.refresh_access_token(self.refresh_token)
return self.access_token
def clear_tokens(self):
self.access_token = None
self.refresh_token = None
self.token_expire = None
# Kullanım örneği
token_manager = TokenManager()
# Başlangıçta token'ları set et (login'den sonra)
token_manager.access_token = "existing_access_token"
token_manager.refresh_token = "existing_refresh_token"
token_manager.token_expire = 1692789600
try:
valid_token = token_manager.get_valid_token()
print(f"Geçerli token: {valid_token[:20]}...")
except Exception as e:
print(f"Token alma hatası: {e}")
PHP
<?php
class TokenManager {
private $accessToken;
private $refreshToken;
private $tokenExpire;
public function __construct() {
session_start();
$this->loadTokensFromSession();
}
private function loadTokensFromSession() {
$this->accessToken = $_SESSION['access_token'] ?? null;
$this->refreshToken = $_SESSION['refresh_token'] ?? null;
$this->tokenExpire = $_SESSION['token_expire'] ?? null;
}
private function saveTokensToSession() {
$_SESSION['access_token'] = $this->accessToken;
$_SESSION['refresh_token'] = $this->refreshToken;
$_SESSION['token_expire'] = $this->tokenExpire;
}
public function refreshAccessToken($refreshToken) {
$url = 'https://integration.seoauthor.ai/api/v1/auth/refresh';
$data = ['refreshToken' => $refreshToken];
$options = [
'http' => [
'header' => [
"Content-Type: application/json",
"User-Agent: PHP TokenManager"
],
'method' => 'POST',
'content' => json_encode($data)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) {
throw new Exception('Token yenileme isteği başarısız');
}
// HTTP status code kontrol et
$status_line = $http_response_header[0];
preg_match('{HTTP\/\S*\s(\d{3})}', $status_line, $match);
$status_code = $match[1];
if ($status_code == 401) {
$this->clearTokens();
throw new Exception('Refresh token geçersiz. Yeniden giriş gerekli.');
} elseif ($status_code >= 400) {
$error_data = json_decode($result, true);
throw new Exception('Token yenileme hatası: ' . $error_data['message']);
}
$tokenData = json_decode($result, true);
// Token'ları güncelle
$this->accessToken = $tokenData['accessToken'];
$this->refreshToken = $tokenData['refreshToken'];
$this->tokenExpire = $tokenData['accessTokenExpire'];
$this->saveTokensToSession();
echo "Token başarıyla yenilendi.\n";
return $tokenData;
}
public function isTokenExpired($bufferSeconds = 300) { // 5 dakika buffer
if (!$this->tokenExpire) {
return true;
}
$currentTime = time();
return ($this->tokenExpire - $currentTime) <= $bufferSeconds;
}
public function getValidToken() {
if (!$this->accessToken || !$this->refreshToken) {
throw new Exception('Token\'lar mevcut değil. Giriş yapın.');
}
if ($this->isTokenExpired()) {
echo "Token süresi dolmuş, yenileniyor...\n";
$this->refreshAccessToken($this->refreshToken);
}
return $this->accessToken;
}
public function clearTokens() {
$this->accessToken = null;
$this->refreshToken = null;
$this->tokenExpire = null;
unset($_SESSION['access_token']);
unset($_SESSION['refresh_token']);
unset($_SESSION['token_expire']);
}
public function getTimeUntilExpiry() {
if (!$this->tokenExpire) {
return 'Token bilgisi yok';
}
$currentTime = time();
$secondsLeft = $this->tokenExpire - $currentTime;
if ($secondsLeft <= 0) {
return 'Token süresi dolmuş';
}
$minutes = floor($secondsLeft / 60);
$hours = floor($minutes / 60);
if ($hours > 0) {
return $hours . ' saat ' . ($minutes % 60) . ' dakika';
} else {
return $minutes . ' dakika';
}
}
}
// Kullanım örneği
try {
$tokenManager = new TokenManager();
// Token'ları manuel set et (login'den sonra yapılır)
$_SESSION['access_token'] = 'existing_access_token';
$_SESSION['refresh_token'] = 'existing_refresh_token';
$_SESSION['token_expire'] = time() + 3600; // 1 saat sonra
$validToken = $tokenManager->getValidToken();
echo "Geçerli token: " . substr($validToken, 0, 20) . "...\n";
echo "Kalan süre: " . $tokenManager->getTimeUntilExpiry() . "\n";
} catch (Exception $e) {
echo "Hata: " . $e->getMessage() . "\n";
}
?>
Axios Interceptor (JavaScript)
// Axios ile otomatik token yenileme
import axios from "axios";
class APIClient {
constructor() {
this.api = axios.create({
baseURL: "https://integration.seoauthor.ai",
timeout: 10000,
});
this.refreshPromise = null;
this.setupInterceptors();
}
setupInterceptors() {
// Request interceptor - her istekte token ekle
this.api.interceptors.request.use(
async (config) => {
const token = await this.getValidToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor - 401 hatalarında token yenile
this.api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
await this.refreshToken();
const newToken = sessionStorage.getItem("accessToken");
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return this.api(originalRequest);
} catch (refreshError) {
// Refresh de başarısızsa, login'e yönlendir
this.clearTokens();
window.location.href = "/login";
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
}
async getValidToken() {
const accessToken = sessionStorage.getItem("accessToken");
const refreshToken = sessionStorage.getItem("refreshToken");
const tokenExpire = parseInt(sessionStorage.getItem("tokenExpire"));
if (!accessToken || !refreshToken) {
return null;
}
// Token süresi kontrol et (5 dakika buffer)
const now = Math.floor(Date.now() / 1000);
if (now + 300 >= tokenExpire) {
await this.refreshToken();
return sessionStorage.getItem("accessToken");
}
return accessToken;
}
async refreshToken() {
const refreshToken = sessionStorage.getItem("refreshToken");
if (!refreshToken) {
throw new Error("No refresh token");
}
// Eğer zaten refresh işlemi devam ediyorsa, onu bekle
if (this.refreshPromise) {
return this.refreshPromise;
}
this.refreshPromise = this.performRefresh(refreshToken);
try {
return await this.refreshPromise;
} finally {
this.refreshPromise = null;
}
}
async performRefresh(refreshToken) {
try {
const response = await axios.post(
"https://integration.seoauthor.ai/api/v1/auth/refresh",
{ refreshToken },
{ headers: { "Content-Type": "application/json" } }
);
const {
accessToken,
refreshToken: newRefreshToken,
accessTokenExpire,
} = response.data;
sessionStorage.setItem("accessToken", accessToken);
sessionStorage.setItem("refreshToken", newRefreshToken);
sessionStorage.setItem("tokenExpire", accessTokenExpire);
return accessToken;
} catch (error) {
this.clearTokens();
throw error;
}
}
clearTokens() {
sessionStorage.removeItem("accessToken");
sessionStorage.removeItem("refreshToken");
sessionStorage.removeItem("tokenExpire");
}
}
// Kullanım
const apiClient = new APIClient();
// Artık tüm API çağrıları otomatik olarak token yönetimi yapacak
apiClient.api
.get("/api/v1/profil/guncel-bakiye")
.then((response) => console.log(response.data))
.catch((error) => console.error(error));
🔧 En İyi Uygulamalar
1. Proactive Token Refresh
Token süresi dolmadan önce yenileyin:
// Token'ı sona ermeden 5 dakika önce yenile
const shouldRefresh = (tokenExpire) => {
const now = Math.floor(Date.now() / 1000);
const fiveMinutes = 5 * 60;
return tokenExpire - now <= fiveMinutes;
};
2. Race Condition'ları Önleme
Aynı anda birden fazla refresh işlemi başlatmayın:
let refreshPromise = null;
async function safeRefreshToken(refreshToken) {
if (refreshPromise) {
return refreshPromise;
}
refreshPromise = refreshAccessToken(refreshToken);
try {
return await refreshPromise;
} finally {
refreshPromise = null;
}
}
3. Hata Durumunda Fallback
Refresh token da geçersizse kullanıcıyı login'e yönlendirin:
try {
await refreshToken();
} catch (error) {
if (error.status === 401) {
// Kullanıcıyı login sayfasına yönlendir
clearAllTokens();
redirectToLogin();
}
}
⏰ Token Süreleri
| Token Türü | Tipik Süre | Önerilen Yenileme |
|---|---|---|
| Access Token | 15-60 dakika | Sona ermeden 5 dakika önce |
| Refresh Token | 7-30 gün | Her access token yenileme ile birlikte |
Token yenileme işlemlerini background'da yapın ve kullanıcı deneyimini kesintiye uğratmayın. Axios interceptor'ları bu konuda çok faydalıdır.
Refresh token da sınırlı süreye sahiptir. Refresh token'ın süresi de dolmuşsa, kullanıcının tekrar giriş yapması gerekir.
Her başarılı refresh işleminde hem access token hem de refresh token yenilenir. Bu güvenlik açısından önemlidir ve token rotation olarak bilinir.