Hızlı Başlangıç Rehberi
Bu rehber, Atlantic ID'yi projenize entegre etmek için gerekli tüm adımları içerir. Yaklaşık 10 dakikada ilk test entegrasyonunuzu tamamlayabilirsiniz.
Başlamadan Önce
Gereksinimler
- Atlantic ID Developer Console hesabı
- HTTPS destekli bir web uygulaması (production için)
- Temel OAuth 2.0 / OIDC bilgisi (önerilir)
Öğrenecekleriniz
Bu rehberde şunları öğreneceksiniz:
- Developer Console'da client oluşturma
- PKCE ile authorization flow başlatma
- Token exchange yapma
- Kullanıcı bilgilerini alma
- Token'ları doğrulama
Adım 1: Developer Console'da Client Oluşturma
1.1. Console'a Giriş Yapın
https://id.codeatlantis.com/dev adresine gidin ve Atlantic ID hesabınızla giriş yapın.
1.2. Yeni Client Oluşturun
"Create New Client" butonuna tıklayın ve formu doldurun:
Client Bilgileri
Client Name:
My Application (Development)
Kullanıcılara gösterilen uygulama adı
Client Type:
- Public: Mobil uygulamalar, SPA (React, Vue, Angular)
- Confidential: Backend uygulamalar (Node.js, PHP, Python, Java)
💡 İpucu: Client secret'ı güvenli saklayamıyorsanız (frontend apps) "Public" seçin.
Redirect URIs:
http://localhost:3000/auth/callback
https://yourapp.com/auth/callback
Her satıra bir URI. Geliştirme ve production URI'lerini şimden ekleyin.
Allowed Scopes:
openid profile email phone offline_access
Başlangıç için tüm scope'ları seçebilirsiniz.
1.3. Client Credentials'ı Kaydedin
Client oluşturduktan sonra aşağıdaki bilgileri not edin:
Client ID: cli_abc123xyz789
Client Secret: sec_def456uvw012 (sadece Confidential için)
⚠️ UYARI:
client_secretdeğerini asla frontend kodunda, Git repository'sinde veya public yerlerde paylaşmayın!
Adım 2: PKCE Implementation
PKCE (Proof Key for Code Exchange), authorization code'un çalınmasını engelleyen bir güvenlik mekanizmasıdır. Public client'lar için zorunludur.
2.1. Code Verifier Oluşturma
JavaScript/TypeScript:
// 43-128 karakter arası rastgele string
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
function base64UrlEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
const codeVerifier = generateCodeVerifier();
// Örnek: "dBjftJeZ4CVP-mB92K27uhbUbP1E_4jY3F_EA2ZXCUE"
Python:
import secrets
import hashlib
import base64
def generate_code_verifier():
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8')
return code_verifier.rstrip('=')
code_verifier = generate_code_verifier()
PHP:
function generateCodeVerifier(): string {
$randomBytes = random_bytes(32);
return rtrim(strtr(base64_encode($randomBytes), '+/', '-_'), '=');
}
$codeVerifier = generateCodeVerifier();
2.2. Code Challenge Oluşturma
Code verifier'ın SHA256 hash'ini alın:
JavaScript/TypeScript:
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(new Uint8Array(hash));
}
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Örnek: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
Python:
def generate_code_challenge(verifier):
digest = hashlib.sha256(verifier.encode('utf-8')).digest()
challenge = base64.urlsafe_b64encode(digest).decode('utf-8')
return challenge.rstrip('=')
code_challenge = generate_code_challenge(code_verifier)
PHP:
function generateCodeChallenge(string $verifier): string {
$hash = hash('sha256', $verifier, true);
return rtrim(strtr(base64_encode($hash), '+/', '-_'), '=');
}
$codeChallenge = generateCodeChallenge($codeVerifier);
2.3. Code Verifier'ı Saklayın
Code verifier'ı güvenli bir yerde saklayın (token exchange'de kullanacaksınız):
Browser (Frontend):
sessionStorage.setItem('pkce_verifier', codeVerifier);
Backend (Session):
// Express.js
req.session.pkceVerifier = codeVerifier;
// PHP
$_SESSION['pkce_verifier'] = $codeVerifier;
Adım 3: Authorization Flow Başlatma
3.1. State Parametresi Oluşturma (CSRF Koruması)
function generateState() {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
const state = generateState();
sessionStorage.setItem('oauth_state', state);
3.2. Authorization URL Oluşturma
const authUrl = new URL('https://id.codeatlantis.com/oauth/authorize');
authUrl.searchParams.set('client_id', 'cli_abc123xyz789');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('redirect_uri', 'http://localhost:3000/auth/callback');
authUrl.searchParams.set('state', state);
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
// Opsiyonel: Nonce ekleyebilirsiniz (ID token replay attack koruması)
const nonce = generateState(); // Aynı fonksiyonu kullanabilirsiniz
authUrl.searchParams.set('nonce', nonce);
sessionStorage.setItem('oauth_nonce', nonce);
3.3. Kullanıcıyı Yönlendirme
window.location.href = authUrl.toString();
Oluşan URL örneği:
https://id.codeatlantis.com/oauth/authorize?
client_id=cli_abc123xyz789&
response_type=code&
scope=openid+profile+email&
redirect_uri=http://localhost:3000/auth/callback&
state=xyz789abc&
code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
code_challenge_method=S256&
nonce=def456ghi
Adım 4: Callback Handling
Kullanıcı Atlantic ID'de login olduktan sonra, redirect_uri adresinize şu parametrelerle döner:
http://localhost:3000/auth/callback?code=AQCxxxxxxxxxxxxxx&state=xyz789abc
4.1. State Doğrulama
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const returnedState = urlParams.get('state');
const storedState = sessionStorage.getItem('oauth_state');
if (returnedState !== storedState) {
throw new Error('State mismatch - possible CSRF attack');
}
// Hata kontrolü
const error = urlParams.get('error');
if (error) {
const errorDescription = urlParams.get('error_description');
throw new Error(`OAuth error: ${error} - ${errorDescription}`);
}
4.2. Authorization Code'u Backend'e Gönderme
Frontend'den Backend'e:
// Code'u backend'e gönderin (cookie, header, vb. ile)
const response = await fetch('/api/auth/callback', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
code: code,
code_verifier: sessionStorage.getItem('pkce_verifier')
})
});
const tokens = await response.json();
Adım 5: Token Exchange
Backend'de authorization code'u token'larla değiştirin:
5.1. Token Endpoint'e İstek
Node.js (fetch):
const response = await fetch('https://id.codeatlantis.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: 'http://localhost:3000/auth/callback',
client_id: 'cli_abc123xyz789',
code_verifier: codeVerifier,
// Confidential client için:
// client_secret: 'sec_def456uvw012'
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Token exchange failed: ${error.error}`);
}
const tokens = await response.json();
PHP:
$data = [
'grant_type' => 'authorization_code',
'code' => $authCode,
'redirect_uri' => 'http://localhost:3000/auth/callback',
'client_id' => 'cli_abc123xyz789',
'code_verifier' => $codeVerifier,
// Confidential için:
// 'client_secret' => 'sec_def456uvw012'
];
$ch = curl_init('https://id.codeatlantis.com/oauth/token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
$tokens = json_decode($response, true);
5.2. Token Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImF0bGFudGljX2lkXzIwMjQifQ...",
"token_type": "Bearer",
"expires_in": 900,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImF0bGFudGljX2lkXzIwMjQifQ...",
"refresh_token": "RT_xxxxxxxxxxxxxxxxxxxxxx",
"scope": "openid profile email"
}
5.3. Token'ları Güvenli Saklama
Backend (Önerilen):
// Session'da sakla
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
// Veya encrypted cookie
res.cookie('access_token', tokens.access_token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 900000 // 15 dakika
});
Adım 6: Kullanıcı Bilgilerini Alma
6.1. UserInfo Endpoint'e İstek
const userResponse = await fetch('https://id.codeatlantis.com/oauth/userinfo', {
headers: {
'Authorization': `Bearer ${tokens.access_token}`
}
});
const user = await userResponse.json();
6.2. User Response
{
"sub": "550e8400-e29b-41d4-a716-446655440000",
"name": "Görkem Yılmaz",
"email": "gorkem@codeatlantis.com",
"email_verified": true,
"phone_number": "+905001234567",
"picture": "https://id.codeatlantis.com/avatar/550e8400.jpg",
"updated_at": 1735686000
}
6.3. Kullanıcıyı Veritabanınıza Kaydetme
// Örnek: User oluştur veya güncelle
const dbUser = await db.users.upsert({
where: { atlanticId: user.sub },
update: {
name: user.name,
email: user.email,
picture: user.picture,
updatedAt: new Date()
},
create: {
atlanticId: user.sub,
name: user.name,
email: user.email,
emailVerified: user.email_verified,
picture: user.picture
}
});
// Session'a user ID'yi kaydet
req.session.userId = dbUser.id;
Adım 7: ID Token Doğrulama
ID token'ı mutlaka backend'de doğrulayın:
7.1. JWKS Public Key ile Doğrulama
Node.js:
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: 'https://id.codeatlantis.com/oauth/jwks',
cache: true,
cacheMaxAge: 86400000
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
callback(null, key.getPublicKey());
});
}
try {
const decoded = await new Promise((resolve, reject) => {
jwt.verify(tokens.id_token, getKey, {
issuer: 'https://id.codeatlantis.com',
audience: 'cli_abc123xyz789',
algorithms: ['RS256']
}, (err, decoded) => {
if (err) reject(err);
else resolve(decoded);
});
});
console.log('Verified user:', decoded.sub, decoded.email);
} catch (error) {
console.error('ID token verification failed:', error);
}
7.2. Nonce Kontrolü (Opsiyonel ama Önerilen)
const storedNonce = sessionStorage.getItem('oauth_nonce');
if (decoded.nonce !== storedNonce) {
throw new Error('Nonce mismatch - possible replay attack');
}
Tam Örnek: React SPA
// src/hooks/useAuth.js
import { useState, useEffect } from 'react';
export function useAuth() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Callback handling
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
if (code) {
handleCallback(code);
} else {
checkSession();
}
}, []);
async function handleCallback(code) {
try {
const verifier = sessionStorage.getItem('pkce_verifier');
const state = urlParams.get('state');
const storedState = sessionStorage.getItem('oauth_state');
if (state !== storedState) throw new Error('State mismatch');
// Backend'e code gönder
const response = await fetch('/api/auth/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, code_verifier: verifier })
});
const { user } = await response.json();
setUser(user);
// Cleanup
sessionStorage.removeItem('pkce_verifier');
sessionStorage.removeItem('oauth_state');
window.history.replaceState({}, '', '/');
} catch (error) {
console.error('Auth failed:', error);
} finally {
setLoading(false);
}
}
async function checkSession() {
try {
const response = await fetch('/api/auth/me');
if (response.ok) {
const { user } = await response.json();
setUser(user);
}
} finally {
setLoading(false);
}
}
async function login() {
const { verifier, challenge } = await generatePKCE();
const state = generateRandomString();
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('oauth_state', state);
const authUrl = new URL('https://id.codeatlantis.com/oauth/authorize');
authUrl.searchParams.set('client_id', process.env.REACT_APP_CLIENT_ID);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('redirect_uri', window.location.origin + '/auth/callback');
authUrl.searchParams.set('state', state);
authUrl.searchParams.set('code_challenge', challenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authUrl.toString();
}
async function logout() {
await fetch('/api/auth/logout', { method: 'POST' });
setUser(null);
}
return { user, loading, login, logout };
}
Sık Karşılaşılan Hatalar
redirect_uri_mismatch
Neden: Redirect URI console'dakiyle eşleşmiyor
Çözüm: Console'dan URI'yi kontrol edin, protocol ve trailing slash dikkat
invalid_grant
Neden: Authorization code geçersiz/expired
Çözüm: Code'lar tek kullanımlık ve 5 dk geçerli
invalid_request (code_challenge required)
Neden: PKCE parametreleri eksik
Çözüm: code_challenge ve code_challenge_method gönderin
Sonraki Adımlar
✅ Tebrikler! İlk entegrasyonunuzu tamamladınız.
Şimdi bunları inceleyin:
- OAuth 2.0 & OIDC Detayları
- JWT Token Yapısı
- SDK Örnekleri
- Güvenlik Best Practices
- Production Checklist
Yardım
Takıldığınız bir yer mi var?
- 📧 developers@codeatlantis.com
- 💬 Discord Community
- 📚 GitHub Examples