SDK ve Kütüphane Örnekleri

Atlantic ID entegrasyonu için popüler dillerde hazır kod örnekleri.


JavaScript / TypeScript

Node.js (Backend)

Kurulum

npm install openid-client express express-session

Tam Örnek

const express = require('express');
const session = require('express-session');
const { Issuer, generators } = require('openid-client');

const app = express();

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: true, httpOnly: true }
}));

// Atlantic ID Client Setup
let atlanticIdClient;

(async () => {
  const issuer = await Issuer.discover('https://id.codeatlantis.com');

  atlanticIdClient = new issuer.Client({
    client_id: process.env.CLIENT_ID,
    client_secret: process.env.CLIENT_SECRET,
    redirect_uris: ['https://myapp.com/auth/callback'],
    response_types: ['code']
  });
})();

// Login Route
app.get('/auth/login', (req, res) => {
  const codeVerifier = generators.codeVerifier();
  const codeChallenge = generators.codeChallenge(codeVerifier);
  const state = generators.state();
  const nonce = generators.nonce();

  req.session.codeVerifier = codeVerifier;
  req.session.state = state;
  req.session.nonce = nonce;

  const authUrl = atlanticIdClient.authorizationUrl({
    scope: 'openid profile email',
    code_challenge: codeChallenge,
    code_challenge_method: 'S256',
    state,
    nonce
  });

  res.redirect(authUrl);
});

// Callback Route
app.get('/auth/callback', async (req, res) => {
  try {
    const params = atlanticIdClient.callbackParams(req);

    const tokenSet = await atlanticIdClient.callback(
      'https://myapp.com/auth/callback',
      params,
      {
        code_verifier: req.session.codeVerifier,
        state: req.session.state,
        nonce: req.session.nonce
      }
    );

    const userinfo = await atlanticIdClient.userinfo(tokenSet.access_token);

    req.session.user = {
      id: userinfo.sub,
      email: userinfo.email,
      name: userinfo.name
    };

    req.session.accessToken = tokenSet.access_token;
    req.session.refreshToken = tokenSet.refresh_token;

    res.redirect('/dashboard');
  } catch (error) {
    console.error('Auth failed:', error);
    res.redirect('/login?error=auth_failed');
  }
});

// Protected Route
app.get('/dashboard', (req, res) => {
  if (!req.session.user) {
    return res.redirect('/login');
  }
  res.json({ user: req.session.user });
});

// Logout
app.post('/auth/logout', (req, res) => {
  req.session.destroy();
  res.redirect('/');
});

app.listen(3000);

React (Frontend SPA)

Kurulum

npm install react-router-dom

Hook: useAuth.js

import { useState, useEffect, createContext, useContext } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Check for callback
    const params = new URLSearchParams(window.location.search);
    const code = params.get('code');

    if (code) {
      handleCallback(code);
    } else {
      checkSession();
    }
  }, []);

  async function handleCallback(code) {
    try {
      const verifier = sessionStorage.getItem('pkce_verifier');
      const state = params.get('state');
      const storedState = sessionStorage.getItem('oauth_state');

      if (state !== storedState) {
        throw new Error('State mismatch');
      }

      // Backend'e gönder
      const response = await fetch('/api/auth/callback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code, code_verifier: verifier })
      });

      const { user } = await response.json();
      setUser(user);

      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 + '/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 (
    <AuthContext.Provider value={{ user, loading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  return useContext(AuthContext);
}

// Helper functions
async function generatePKCE() {
  const verifier = generateCodeVerifier();
  const challenge = await generateCodeChallenge(verifier);
  return { verifier, challenge };
}

function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return base64UrlEncode(array);
}

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));
}

function base64UrlEncode(buffer) {
  return btoa(String.fromCharCode(...buffer))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

function generateRandomString() {
  return base64UrlEncode(crypto.getRandomValues(new Uint8Array(16)));
}

Kullanım

import { AuthProvider, useAuth } from './useAuth';

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
        </Routes>
      </Router>
    </AuthProvider>
  );
}

function ProtectedRoute({ children }) {
  const { user, loading } = useAuth();

  if (loading) return <div>Loading...</div>;
  if (!user) return <Navigate to="/" />;

  return children;
}

function Home() {
  const { user, login } = useAuth();

  if (user) return <Navigate to="/dashboard" />;

  return (
    <div>
      <h1>Welcome</h1>
      <button onClick={login}>Login with Atlantic ID</button>
    </div>
  );
}

function Dashboard() {
  const { user, logout } = useAuth();

  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <p>Email: {user.email}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

PHP

Laravel

Kurulum

composer require league/oauth2-client

Config (config/services.php)

return [
    'atlantic_id' => [
        'client_id' => env('ATLANTIC_ID_CLIENT_ID'),
        'client_secret' => env('ATLANTIC_ID_CLIENT_SECRET'),
        'redirect' => env('ATLANTIC_ID_REDIRECT_URI'),
    ],
];

Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use League\OAuth2\Client\Provider\GenericProvider;

class AuthController extends Controller
{
    private function getProvider()
    {
        return new GenericProvider([
            'clientId' => config('services.atlantic_id.client_id'),
            'clientSecret' => config('services.atlantic_id.client_secret'),
            'redirectUri' => config('services.atlantic_id.redirect'),
            'urlAuthorize' => 'https://id.codeatlantis.com/oauth/authorize',
            'urlAccessToken' => 'https://id.codeatlantis.com/oauth/token',
            'urlResourceOwnerDetails' => 'https://id.codeatlantis.com/oauth/userinfo',
            'scopes' => 'openid profile email',
        ]);
    }

    public function login(Request $request)
    {
        $provider = $this->getProvider();

        // PKCE
        $codeVerifier = bin2hex(random_bytes(32));
        $codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');

        session([
            'oauth2_code_verifier' => $codeVerifier,
            'oauth2_state' => $provider->getState(),
        ]);

        $authUrl = $provider->getAuthorizationUrl([
            'code_challenge' => $codeChallenge,
            'code_challenge_method' => 'S256',
        ]);

        return redirect($authUrl);
    }

    public function callback(Request $request)
    {
        $provider = $this->getProvider();

        if ($request->get('state') !== session('oauth2_state')) {
            session()->forget(['oauth2_state', 'oauth2_code_verifier']);
            return redirect('/')->withErrors(['error' => 'Invalid state']);
        }

        try {
            $accessToken = $provider->getAccessToken('authorization_code', [
                'code' => $request->get('code'),
                'code_verifier' => session('oauth2_code_verifier'),
            ]);

            $resourceOwner = $provider->getResourceOwner($accessToken);
            $userData = $resourceOwner->toArray();

            // Create or update user
            $user = \App\Models\User::updateOrCreate(
                ['atlantic_id' => $userData['sub']],
                [
                    'name' => $userData['name'],
                    'email' => $userData['email'],
                    'email_verified_at' => now(),
                ]
            );

            auth()->login($user);

            session()->forget(['oauth2_state', 'oauth2_code_verifier']);

            return redirect('/dashboard');
        } catch (\Exception $e) {
            return redirect('/')->withErrors(['error' => 'Authentication failed']);
        }
    }

    public function logout()
    {
        auth()->logout();
        return redirect('/');
    }
}

Routes (routes/web.php)

Route::get('/auth/login', [AuthController::class, 'login'])->name('auth.login');
Route::get('/auth/callback', [AuthController::class, 'callback'])->name('auth.callback');
Route::post('/auth/logout', [AuthController::class, 'logout'])->name('auth.logout');

Python

Django

Kurulum

pip install authlib requests

views.py

from django.shortcuts import redirect
from django.http import JsonResponse
from authlib.integrations.django_client import OAuth
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
import os

oauth = OAuth()

oauth.register(
    name='atlantic_id',
    client_id=os.getenv('ATLANTIC_ID_CLIENT_ID'),
    client_secret=os.getenv('ATLANTIC_ID_CLIENT_SECRET'),
    server_metadata_url='https://id.codeatlantis.com/.well-known/openid-configuration',
    client_kwargs={
        'scope': 'openid profile email',
        'code_challenge_method': 'S256',
    }
)

def login_view(request):
    redirect_uri = request.build_absolute_uri('/auth/callback')
    return oauth.atlantic_id.authorize_redirect(request, redirect_uri)

def callback_view(request):
    try:
        token = oauth.atlantic_id.authorize_access_token(request)
        userinfo = token['userinfo']

        # Create or get user
        user, created = User.objects.get_or_create(
            username=userinfo['sub'],
            defaults={
                'email': userinfo['email'],
                'first_name': userinfo['name'].split()[0] if userinfo.get('name') else '',
            }
        )

        login(request, user)
        return redirect('/dashboard')
    except Exception as e:
        return redirect('/?error=auth_failed')

@login_required
def logout_view(request):
    logout(request)
    return redirect('/')

urls.py

urlpatterns = [
    path('auth/login', login_view, name='login'),
    path('auth/callback', callback_view, name='callback'),
    path('auth/logout', logout_view, name='logout'),
]

Go

Fiber Framework

Kurulum

go get github.com/gofiber/fiber/v2
go get github.com/coreos/go-oidc/v3/oidc
go get golang.org/x/oauth2

main.go

package main

import (
    "context"
    "crypto/rand"
    "crypto/sha256"
    "encoding/base64"
    "log"

    "github.com/coreos/go-oidc/v3/oidc"
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/session"
    "golang.org/x/oauth2"
)

var (
    config *oauth2.Config
    verifier *oidc.IDTokenVerifier
    store *session.Store
)

func init() {
    ctx := context.Background()
    provider, _ := oidc.NewProvider(ctx, "https://id.codeatlantis.com")

    config = &oauth2.Config{
        ClientID:     os.Getenv("CLIENT_ID"),
        ClientSecret: os.Getenv("CLIENT_SECRET"),
        RedirectURL:  "https://myapp.com/auth/callback",
        Endpoint:     provider.Endpoint(),
        Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
    }

    verifier = provider.Verifier(&oidc.Config{ClientID: config.ClientID})
    store = session.New()
}

func main() {
    app := fiber.New()

    app.Get("/auth/login", login)
    app.Get("/auth/callback", callback)
    app.Post("/auth/logout", logout)

    log.Fatal(app.Listen(":3000"))
}

func login(c *fiber.Ctx) error {
    sess, _ := store.Get(c)

    // PKCE
    verifier := generateCodeVerifier()
    challenge := generateCodeChallenge(verifier)
    state := generateRandomString(32)

    sess.Set("code_verifier", verifier)
    sess.Set("state", state)
    sess.Save()

    authURL := config.AuthCodeURL(state,
        oauth2.SetAuthURLParam("code_challenge", challenge),
        oauth2.SetAuthURLParam("code_challenge_method", "S256"),
    )

    return c.Redirect(authURL)
}

func callback(c *fiber.Ctx) error {
    sess, _ := store.Get(c)

    code := c.Query("code")
    state := c.Query("state")
    storedState := sess.Get("state").(string)

    if state != storedState {
        return c.SendStatus(400)
    }

    verifier := sess.Get("code_verifier").(string)

    token, err := config.Exchange(context.Background(), code,
        oauth2.SetAuthURLParam("code_verifier", verifier),
    )
    if err != nil {
        return c.SendStatus(500)
    }

    // Verify ID Token
    rawIDToken := token.Extra("id_token").(string)
    idToken, err := verifier.Verify(context.Background(), rawIDToken)
    if err != nil {
        return c.SendStatus(500)
    }

    var claims struct {
        Sub   string `json:"sub"`
        Email string `json:"email"`
        Name  string `json:"name"`
    }
    idToken.Claims(&claims)

    sess.Set("user", claims)
    sess.Delete("code_verifier")
    sess.Delete("state")
    sess.Save()

    return c.Redirect("/dashboard")
}

func logout(c *fiber.Ctx) error {
    sess, _ := store.Get(c)
    sess.Destroy()
    return c.Redirect("/")
}

func generateCodeVerifier() string {
    b := make([]byte, 32)
    rand.Read(b)
    return base64.RawURLEncoding.EncodeToString(b)
}

func generateCodeChallenge(verifier string) string {
    h := sha256.New()
    h.Write([]byte(verifier))
    return base64.RawURLEncoding.EncodeToString(h.Sum(nil))
}

func generateRandomString(length int) string {
    b := make([]byte, length)
    rand.Read(b)
    return base64.RawURLEncoding.EncodeToString(b)
}

Tüm örnekler GitHub'da: github.com/codeatlantis/atlantic-id-examples

İlgili: Getting Started, API Reference