Ana içeriğe geç

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

ParametreTipZorunluAçıklama
refreshTokenstringDaha ö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

AlanTipAçıklama
accessTokenstringYeni API access token
accessTokenExpireintegerYeni token'ın sona erme zamanı (Unix timestamp)
refreshTokenstringYeni 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 Token15-60 dakikaSona ermeden 5 dakika önce
Refresh Token7-30 günHer access token yenileme ile birlikte
Pro İpucu

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.

Önemli

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.

Bilgi

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.