Zum Hauptinhalt springen

Documentation Index

Fetch the complete documentation index at: https://docs.aihubmix.com/llms.txt

Use this file to discover all available pages before exploring further.

Wichtiger Sicherheitshinweis

Grundsatz „Sicherheit zuerst“: Bei der Entwicklung und Bereitstellung von Drittanbieteranwendungen ist Sicherheit der wichtigste Aspekt. Sie müssen die nachstehenden Sicherheitsprinzipien strikt einhalten, um die Sicherheit der Nutzerdaten und des Autorisierungsprozesses zu gewährleisten.
  1. Geben Sie client_secret niemals in JavaScript-Code im Frontend preis
  2. Der Austausch des OAuth-Authorization-Codes gegen ein Token muss serverseitig erfolgen
  3. Jeglicher Zugriff auf geschützte Nutzerressourcen muss über ein Backend-API-Proxy laufen
  4. Die gesamte OAuth-Kommunikation muss durch HTTPS geschützt werden
Ein Verstoß gegen eines der oben genannten Sicherheitsprinzipien kann zu schwerwiegenden Sicherheitslücken führen!

📋 Inhaltsverzeichnis

  1. Überblick
  2. Erste Schritte
  3. OAuth2-Autorisierungsablauf
  4. API-Referenz
  5. SDK und Codebeispiele
  6. Best Practices für die Sicherheit
  7. FAQ
  8. Technischer Support

Überblick

Wir bieten eine offene API auf Basis des OAuth2-Standards an, die es Drittanbieteranwendungen ermöglicht, sicher auf die Basisinformationen und den Kontostand der Nutzer zuzugreifen. Über unseren OAuth2-Dienst kann Ihre Anwendung:
  • 🚀 Ein-Klick-Anmeldung: Nutzer müssen sich nicht erneut registrieren; die Anmeldung erteilt die Autorisierung automatisch und sorgt für ein wirklich nahtloses Erlebnis
  • 👤 Nutzerinformationen abrufen: Zugriff auf das Basisprofil der Nutzer (Benutzername, E-Mail usw.)
  • 💰 Kontostand einsehen: Den Kontostand der Nutzer in Echtzeit abrufen
  • 🔄 Aufladungs-Weiterleitung: Nutzer zu unserer Aufladungsseite leiten, um das Konto aufzuladen
  • 🔐 Automatische Token-Erneuerung: Eingebauter Refresh-Token-Mechanismus für eine nahtlose Token-Erneuerung und ein besseres Nutzererlebnis

Erste Schritte

1. Entwicklerkonto registrieren

Zunächst müssen Sie ein Entwicklerkonto in unserem System registrieren.

2. OAuth-Anwendung erstellen

Erstellen Sie Ihre OAuth-Anwendung in der Entwicklerkonsole:
API call example
curl -X POST https://api.aihubmix.com/api/oauth_apps \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_DEVELOPER_TOKEN" \
  -d '{
    "name": "My Third-party App",
    "description": "App description",
    "redirect_uri": "https://yourapp.com/oauth/callback"
  }'
Beispielantwort:
{
  "success": true,
  "message": "Application created successfully",
  "data": {
    "id": 1,
    "name": "My Third-party App",
    "client_id": "client_abc123def456...",
    "client_secret": "secret_xyz789uvw012...",
    "redirect_uri": "https://yourapp.com/oauth/callback",
    "created_time": 1640995200
  }
}
Wichtiger Sicherheitshinweis:
  • client_id darf im Frontend verwendet werden (öffentliche Information)
  • client_secret darf nur serverseitig verwendet und niemals im Browser offengelegt werden
  • Speichern Sie client_secret in Umgebungsvariablen; codieren Sie es nicht fest in Ihren Code ein

3. Redirect-URI konfigurieren

Stellen Sie sicher, dass Ihre Redirect-URI die folgenden Anforderungen erfüllt:
  • Verwendet das HTTPS-Protokoll (für die Produktion)
  • Verweist auf einen Endpunkt Ihres Servers (keine Frontend-Seite)
  • Die Domain ist registriert und erreichbar
  • Der Pfad ist spezifisch für den API-Endpunkt, der den Callback verarbeitet

OAuth2-Autorisierungsablauf

Sicheres Ablaufdiagramm

Schritt-für-Schritt-Anleitung

Schritt 1: Nutzer zur Autorisierung führen

Fügen Sie auf Ihrer Frontend-Seite eine Anmeldeschaltfläche hinzu. Beim Klick erfolgt eine Weiterleitung auf den serverseitigen Autorisierungs-Endpunkt:
Frontend code - only responsible for redirecting
function startLogin() {
    // Redirect to your server-side authorization handler
    window.location.href = '/auth/oauth/start';
}

Schritt 2: Serverseitiger Autorisierungs-Handler

Implementieren Sie den Autorisierungs-Handler auf Ihrem Server:
Server-side code
app.get('/auth/oauth/start', (req, res) => {
    // Generate and store state parameter
    const state = generateSecureRandomString();
    req.session.oauth_state = state;
    
    // Build authorization URL
    const authUrl = new URL('https://your-domain.com/api/oauth2/authorize');
    authUrl.searchParams.append('client_id', process.env.OAUTH_CLIENT_ID);
    authUrl.searchParams.append('redirect_uri', process.env.OAUTH_REDIRECT_URI);
    authUrl.searchParams.append('response_type', 'code');
    authUrl.searchParams.append('scope', 'profile balance');
    authUrl.searchParams.append('state', state);
    authUrl.searchParams.append('auto_authorize', 'true'); // One-click login
    
    // Redirect to authorization server
    res.redirect(authUrl.toString());
});

Schritt 3: Autorisierungs-Callback verarbeiten (serverseitig)

Server-side authorization callback handler
app.get('/oauth/callback', async (req, res) => {
    const { code, state, error } = req.query;
    
    // Error handling
    if (error) {
        return res.redirect(`/?error=${encodeURIComponent(error)}`);
    }
    
    // Parameter validation
    if (!code || !state) {
        return res.redirect('/?error=missing_parameters');
    }
    
    // Validate state parameter (prevent CSRF attacks)
    if (state !== req.session.oauth_state) {
        return res.redirect('/?error=invalid_state');
    }
    
    try {
        // Exchange authorization code for access token (server-side)
        const tokenResponse = await fetch('https://your-domain.com/api/oauth2/token', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams({
                grant_type: 'authorization_code',
                code: code,
                redirect_uri: process.env.OAUTH_REDIRECT_URI,
                client_id: process.env.OAUTH_CLIENT_ID,
                client_secret: process.env.OAUTH_CLIENT_SECRET // Server-side only
            })
        });
        
        const tokenData = await tokenResponse.json();
        
        if (!tokenResponse.ok) {
            throw new Error(tokenData.error || 'Token exchange failed');
        }
        
        // Securely store token (server session or database)
        req.session.access_token = tokenData.access_token;
        req.session.refresh_token = tokenData.refresh_token;
        req.session.token_expires_at = Date.now() + (tokenData.expires_in * 1000);
        
        // Clean up temporary state
        delete req.session.oauth_state;
        
        // Redirect to frontend page
        res.redirect('/?login=success');
        
    } catch (error) {
        console.error('OAuth callback error:', error);
        res.redirect(`/?error=server_error`);
    }
});

Schritt 4: Frontend ruft Nutzerinformationen ab

Frontend fetches user info via API proxy
async function loadUserInfo() {
    try {
        const response = await fetch('/api/user/info');
        
        if (!response.ok) {
            throw new Error('Failed to fetch user info');
        }
        
        const userInfo = await response.json();
        displayUserInfo(userInfo);
        
    } catch (error) {
        console.error('Failed to load user info:', error);
        showLoginButton();
    }
}
Server-side API proxy
app.get('/api/user/info', async (req, res) => {
    const accessToken = req.session.access_token;
    
    if (!accessToken) {
        return res.status(401).json({ error: 'Not authenticated' });
    }
    
    try {
        // Proxy request to OAuth server
        const response = await fetch('https://your-domain.com/api/oauth2/userinfo', {
            headers: {
                'Authorization': `Bearer ${accessToken}`
            }
        });
        
        if (!response.ok) {
            throw new Error('User info request failed');
        }
        
        const userInfo = await response.json();
        res.json(userInfo);
        
    } catch (error) {
        console.error('User info proxy error:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

API-Referenz

1. Autorisierungs-Endpunkt

GET /api/oauth2/authorize Führt den Nutzer durch die OAuth2-Autorisierung. Parameter:
ParameterTypErforderlichBeschreibung
client_idstringJaClient-ID der Anwendung (im Frontend verwendbar)
redirect_uristringJaCallback-URI nach der Autorisierung (muss auf einen Server-Endpunkt verweisen)
response_typestringJaFest: code
scopestringNeinBerechtigungsumfang, durch Leerzeichen getrennt
statestringJaZufallszeichenfolge zur Abwehr von CSRF-Angriffen (serverseitig generiert)
auto_authorizestringNeinAuf true setzen, um die automatische Autorisierung zu aktivieren
Sicherheitsanforderungen:
  • redirect_uri muss exakt mit der registrierten Adresse übereinstimmen
  • state muss eine serverseitig generierte Zufallszeichenfolge sein
  • HTTPS muss verwendet werden (in der Produktion)
Erläuterung der Scopes:
  • profile: Zugriff auf die Basisinformationen des Nutzers (Benutzername, E-Mail)
  • balance: Zugriff auf den Kontostand des Nutzers

2. Token-Endpunkt

POST /api/oauth2/token
Sicherheitswarnung: Dieser Endpunkt darf ausschließlich serverseitig aufgerufen werden, niemals vom Frontend!
Wird in zwei Szenarien verwendet:
  1. Austausch des Authorization-Codes gegen ein Access Token
  2. Verwendung des Refresh Tokens zum Abrufen eines neuen Access Tokens
Parameter für die Authorization-Code-Vergabe:
ParameterTypErforderlichBeschreibung
grant_typestringJaFest: authorization_code
codestringJaAuthorization-Code
redirect_uristringJaMuss mit der bei der Autorisierung verwendeten URI übereinstimmen
client_idstringJaClient-ID der Anwendung
client_secretstringJaClient-Secret der Anwendung (nur serverseitig)
Parameter für die Refresh-Token-Vergabe:
ParameterTypErforderlichBeschreibung
grant_typestringJaFest: refresh_token
refresh_tokenstringJaRefresh Token
client_idstringJaClient-ID der Anwendung
client_secretstringJaClient-Secret der Anwendung (nur serverseitig)
Beispielantwort:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 7200,
  "refresh_token": "refresh_abc123def456...",
  "scope": "profile balance"
}

3. Nutzerinformations-Endpunkt

GET /api/oauth2/userinfo Ruft die Basisinformationen des Nutzers und den Kontostand ab. Anforderungs-Header:
header
Authorization: Bearer {access_token}
Beispielantwort:
response
{
  "id": 12345,
  "username": "user123",
  "email": "user@example.com",
  "quota": 1000000,
  "used_quota": 250000,
  "balance_formatted": "750.00",
  "created_time": 1640995200,
  "status": 1
}

SDK und Codebeispiele

JavaScript-SDK

Wir stellen ein vollständiges JavaScript-SDK bereit, das Sie direkt verwenden können:
<!DOCTYPE html>
<html>
<head>
    <title>Third-party App Example</title>
    <style>
        .container { max-width: 800px; margin: 0 auto; padding: 20px; }
        .user-info { background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0; }
        .login-section { text-align: center; padding: 40px; }
        .btn { padding: 12px 24px; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; }
        .btn-primary { background: #007bff; color: white; }
        .btn-success { background: #28a745; color: white; }
        .btn-secondary { background: #6c757d; color: white; }
        .hidden { display: none; }
    </style>
</head>
<body>
    <div class="container">
        <h1>我的第三方应用</h1>
        
        <!-- Loading status -->
        <div id="loading">
            <p>Checking login status...</p>
        </div>
        
        <!-- Logged-in user info -->
        <div id="user-info" class="user-info hidden">
            <h2>Welcome back!</h2>
            <p><strong>Username:</strong> <span id="username"></span></p>
            <p><strong>Email:</strong> <span id="email"></span></p>
            <p><strong>Account Balance:</strong> <span id="balance"></span></p>
            <div style="margin-top: 20px;">
                <button class="btn btn-success" onclick="refreshBalance()">Refresh Balance</button>
                <button class="btn btn-primary" onclick="goToTopup()">Top Up Account</button>
                <button class="btn btn-secondary" onclick="logout()">Log Out</button>
            </div>
        </div>
        
        <!-- Not logged in status -->
        <div id="login-section" class="login-section hidden">
            <h2>Please log in to view account info</h2>
            <p>Use one-click login for quick access to your account</p>
            <button class="btn btn-primary" onclick="oneClickLogin()">
                🚀 One-click Login
            </button>
        </div>
    </div>

    <script>
        // OAuth configuration
        const OAUTH_CONFIG = {
            authServer: 'https://your-domain.com',
            clientId: 'YOUR_CLIENT_ID',
            clientSecret: 'YOUR_CLIENT_SECRET', // 生产环境应该在后端处理
            redirectUri: window.location.origin + '/oauth/callback.html',
            scope: 'profile balance'
        };

        class OAuthManager {
            constructor() {
                this.accessToken = localStorage.getItem('oauth_access_token');
                this.refreshToken = localStorage.getItem('oauth_refresh_token');
                this.tokenExpiresAt = localStorage.getItem('oauth_token_expires_at');
                this.isRefreshing = false; // 防止并发刷新
                this.init();
            }

            async init() {
                // 检查URL中是否有授权码
                const urlParams = new URLSearchParams(window.location.search);
                const code = urlParams.get('code');
                const state = urlParams.get('state');

                if (code) {
                    await this.handleAuthCallback(code, state);
                    // 清理URL
                    window.history.replaceState({}, document.title, window.location.pathname);
                } else if (this.accessToken) {
                    try {
                        // 检查token是否过期,如果过期尝试刷新
                        if (this.isTokenExpired()) {
                            await this.refreshTokenIfNeeded();
                        }
                        await this.fetchUserInfo();
                        this.showUserInfo();
                    } catch (error) {
                        console.log('Token可能已过期或无效,需要重新登录');
                        this.clearTokens();
                        this.showLoginSection();
                    }
                } else {
                    this.showLoginSection();
                }

                document.getElementById('loading').classList.add('hidden');
            }

            // Check if access token is expired
            isTokenExpired() {
                if (!this.tokenExpiresAt) return false;
                const expiryTime = parseInt(this.tokenExpiresAt);
                const bufferTime = 5 * 60 * 1000; // 5 minutes buffer
                return Date.now() > (expiryTime - bufferTime);
            }

            // Automatically refresh token
            async refreshTokenIfNeeded() {
                if (!this.refreshToken || this.isRefreshing) {
                    return false;
                }

                this.isRefreshing = true;
                
                try {
                    const response = await fetch(`${OAUTH_CONFIG.authServer}/api/oauth2/token`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                        },
                        body: new URLSearchParams({
                            grant_type: 'refresh_token',
                            refresh_token: this.refreshToken,
                            client_id: OAUTH_CONFIG.clientId,
                            client_secret: OAUTH_CONFIG.clientSecret,
                        })
                    });

                    if (response.ok) {
                        const tokenData = await response.json();
                        this.updateTokens(tokenData);
                        return true;
                    } else {
                        throw new Error('Token refresh failed');
                    }
                } catch (error) {
                    console.error('Failed to refresh token:', error);
                    this.clearTokens();
                    return false;
                } finally {
                    this.isRefreshing = false;
                }
            }

            // Update token info
            updateTokens(tokenData) {
                this.accessToken = tokenData.access_token;
                this.refreshToken = tokenData.refresh_token;
                this.tokenExpiresAt = Date.now() + (tokenData.expires_in * 1000);

                localStorage.setItem('oauth_access_token', this.accessToken);
                localStorage.setItem('oauth_refresh_token', this.refreshToken);
                localStorage.setItem('oauth_token_expires_at', this.tokenExpiresAt.toString());
            }

            oneClickLogin() {
                const state = this.generateState();
                localStorage.setItem('oauth_state', state);

                const authUrl = `${OAUTH_CONFIG.authServer}/api/oauth2/authorize?` +
                    `client_id=${OAUTH_CONFIG.clientId}&` +
                    `redirect_uri=${encodeURIComponent(OAUTH_CONFIG.redirectUri)}&` +
                    `response_type=code&` +
                    `scope=${encodeURIComponent(OAUTH_CONFIG.scope)}&` +
                    `state=${state}&` +
                    `auto_authorize=true`; // Enable auto-authorization for true one-click login

                // Open authorization page in popup
                const popup = window.open(authUrl, 'oauth_login', 'width=500,height=600,scrollbars=yes');

                // Listen for popup close
                const checkClosed = setInterval(() => {
                    if (popup.closed) {
                        clearInterval(checkClosed);
                        // 检查是否获得了授权
                        setTimeout(() => this.init(), 1000);
                    }
                }, 1000);
            }

            async handleAuthCallback(code, state) {
                const savedState = localStorage.getItem('oauth_state');
                if (state !== savedState) {
                    console.error('State parameter mismatch');
                    return;
                }

                try {
                    const response = await fetch(`${OAUTH_CONFIG.authServer}/api/oauth2/token`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                        },
                        body: new URLSearchParams({
                            grant_type: 'authorization_code',
                            code: code,
                            client_id: OAUTH_CONFIG.clientId,
                            client_secret: OAUTH_CONFIG.clientSecret,
                            redirect_uri: OAUTH_CONFIG.redirectUri
                        })
                    });

                    const tokenData = await response.json();
                    
                    if (tokenData.access_token) {
                        this.updateTokens(tokenData);
                        localStorage.removeItem('oauth_state');
                        
                        await this.fetchUserInfo();
                        this.showUserInfo();
                    }
                } catch (error) {
                    console.error('获取访问令牌失败:', error);
                }
            }

            // API request method with auto-refresh
            async apiRequest(url, options = {}) {
                // Check and refresh token if needed
                if (this.isTokenExpired()) {
                    const refreshed = await this.refreshTokenIfNeeded();
                    if (!refreshed) {
                        throw new Error('Unable to refresh token');
                    }
                }

                const headers = {
                    'Authorization': `Bearer ${this.accessToken}`,
                    ...options.headers
                };

                const response = await fetch(url, {
                    ...options,
                    headers
                });

                // If a 401 error is received, try to refresh token and retry once
                if (response.status === 401 && !options._retry) {
                    const refreshed = await this.refreshTokenIfNeeded();
                    if (refreshed) {
                        return this.apiRequest(url, { ...options, _retry: true });
                    }
                }

                return response;
            }

            async fetchUserInfo() {
                const response = await this.apiRequest(`${OAUTH_CONFIG.authServer}/api/oauth2/userinfo`);

                if (!response.ok) {
                    throw new Error('Failed to get user info');
                }

                this.userInfo = await response.json();
            }

            showUserInfo() {
                document.getElementById('username').textContent = this.userInfo.username;
                document.getElementById('email').textContent = this.userInfo.email;
                document.getElementById('balance').textContent = this.userInfo.balance_formatted;

                document.getElementById('user-info').classList.remove('hidden');
                document.getElementById('login-section').classList.add('hidden');
            }

            showLoginSection() {
                document.getElementById('user-info').classList.add('hidden');
                document.getElementById('login-section').classList.remove('hidden');
            }

            async refreshBalance() {
                try {
                    await this.fetchUserInfo();
                    document.getElementById('balance').textContent = this.userInfo.balance_formatted;
                    alert('Balance updated');
                } catch (error) {
                    alert('Refresh failed, please log in again');
                    this.clearTokens();
                    this.showLoginSection();
                }
            }

            async goToTopup() {
                try {
                    const response = await this.apiRequest(`${OAUTH_CONFIG.authServer}/api/oauth2/topup`);

                    const data = await response.json();
                    if (data.topup_url) {
                        window.open(data.topup_url, '_blank');
                    }
                } catch (error) {
                    console.error('Failed to get top-up link:', error);
                }
            }

            logout() {
                this.clearTokens();
                this.showLoginSection();
            }

            clearTokens() {
                this.accessToken = null;
                this.refreshToken = null;
                this.tokenExpiresAt = null;
                this.userInfo = null;
                
                localStorage.removeItem('oauth_access_token');
                localStorage.removeItem('oauth_refresh_token');
                localStorage.removeItem('oauth_token_expires_at');
            }

            generateState() {
                return Math.random().toString(36).substring(2, 15) + 
                       Math.random().toString(36).substring(2, 15);
            }
        }

        // 全局函数
        let oauthManager;

        function oneClickLogin() {
            oauthManager.oneClickLogin();
        }

        function refreshBalance() {
            oauthManager.refreshBalance();
        }

        function goToTopup() {
            oauthManager.goToTopup();
        }

        function logout() {
            oauthManager.logout();
        }

        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', () => {
            oauthManager = new OAuthManager();
        });
    </script>
</body>
</html>

OAuth-Callback-Seite

Erstellen Sie eine Datei /oauth/callback.html:
<!DOCTYPE html>
<html>
<head>
    <title>Logging in...</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
        .loading { color: #666; }
        .error { color: #dc3545; }
    </style>
</head>
<body>
        <h2>Logging in, please wait...</h2>
        <div id="status" class="loading">Verifying authorization information...</div>

    <script>
        const urlParams = new URLSearchParams(window.location.search);
        const code = urlParams.get('code');
        const state = urlParams.get('state');
        const error = urlParams.get('error');

        if (error) {
            document.getElementById('status').innerHTML = 
                `<div class="error">Authorization failed: ${error}</div>`;
            setTimeout(() => {
                if (window.opener) {
                    window.close();
                } else {
                    window.location.href = '/';
                }
            }, 3000);
        } else if (code) {
            if (window.opener) {
                // 如果是弹窗,关闭并让父窗口处理
                window.opener.location.href = 
                    window.opener.location.pathname + `?code=${code}&state=${state}`;
                window.close();
            } else {
                // 如果不是弹窗,重定向到主页面
                window.location.href = `/?code=${code}&state=${state}`;
            }
        } else {
            document.getElementById('status').innerHTML = 
                '<div class="error">No valid authorization information received</div>';
            setTimeout(() => {
                window.location.href = '/';
            }, 3000);
        }
    </script>
</body>
</html>

Node.js-Backend-Beispiel

Aus Sicherheitsgründen wird empfohlen, client_secret im Backend zu verarbeiten:
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');

const app = express();
app.use(cors());
app.use(express.json());

const OAUTH_CONFIG = {
    authServer: 'https://your-domain.com',
    clientId: 'YOUR_CLIENT_ID',
    clientSecret: 'YOUR_CLIENT_SECRET'
};

// Backend handles token exchange
app.post('/api/oauth/exchange-token', async (req, res) => {
    const { code, redirectUri, state } = req.body;

    try {
        const response = await fetch(`${OAUTH_CONFIG.authServer}/api/oauth2/token`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                grant_type: 'authorization_code',
                code: code,
                client_id: OAUTH_CONFIG.clientId,
                client_secret: OAUTH_CONFIG.clientSecret,
                redirect_uri: redirectUri
            })
        });

        const tokenData = await response.json();
        
        if (response.ok) {
            res.json(tokenData);
        } else {
            res.status(400).json(tokenData);
        }
    } catch (error) {
        res.status(500).json({ error: 'Token exchange failed' });
    }
});

// Refresh token endpoint
app.post('/api/oauth/refresh-token', async (req, res) => {
    const { refreshToken } = req.body;

    if (!refreshToken) {
        return res.status(400).json({ error: 'Missing refresh token' });
    }

    try {
        const response = await fetch(`${OAUTH_CONFIG.authServer}/api/oauth2/token`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                grant_type: 'refresh_token',
                refresh_token: refreshToken,
                client_id: OAUTH_CONFIG.clientId,
                client_secret: OAUTH_CONFIG.clientSecret,
            })
        });

        const tokenData = await response.json();
        
        if (response.ok) {
            res.json(tokenData);
        } else {
            res.status(400).json(tokenData);
        }
    } catch (error) {
        res.status(500).json({ error: 'Token refresh failed' });
    }
});

// Proxy user info request (with auto-refresh)
app.get('/api/oauth/userinfo', async (req, res) => {
    const authHeader = req.headers.authorization;
    
    if (!authHeader) {
        return res.status(401).json({ error: 'Missing authorization header' });
    }

    try {
        const response = await fetch(`${OAUTH_CONFIG.authServer}/api/oauth2/userinfo`, {
            headers: {
                'Authorization': authHeader
            }
        });

        const userData = await response.json();
        
        if (response.ok) {
            res.json(userData);
        } else {
            res.status(response.status).json(userData);
        }
    } catch (error) {
        res.status(500).json({ error: 'Failed to fetch user info' });
    }
});

app.listen(3000, () => {
    console.log('Server running on port 3000');
});

Best Practices für die Sicherheit

1. Schutz des Client-Secrets

  • ✅ Empfohlen: client_secret auf dem Backend-Server speichern
  • ❌ Vermeiden: client_secret im JavaScript-Code des Frontends offenlegen

2. Validierung des State-Parameters

// Generate random state
const state = crypto.randomBytes(16).toString('hex');
localStorage.setItem('oauth_state', state);

// Validate state
const savedState = localStorage.getItem('oauth_state');
if (receivedState !== savedState) {
    throw new Error('CSRF attack detected');
}

3. Verwendung von HTTPS

  • In der Produktion muss HTTPS verwendet werden
  • Die Callback-URI muss HTTPS verwenden
  • Alle API-Anfragen nutzen HTTPS

4. Sichere Speicherung von Tokens

// Set token expiry time
const expiryTime = Date.now() + (tokenData.expires_in * 1000);
localStorage.setItem('oauth_token_expiry', expiryTime);

// Check if token is expired
function isTokenExpired() {
    const expiry = localStorage.getItem('oauth_token_expiry');
    return !expiry || Date.now() > parseInt(expiry);
}

5. Fehlerbehandlung

try {
    const response = await fetch('/api/oauth2/userinfo', {
        headers: { 'Authorization': `Bearer ${token}` }
    });
    
    if (!response.ok) {
        if (response.status === 401) {
            // Token expired, need to re-login
            clearToken();
            showLoginSection();
        } else {
            throw new Error(`HTTP ${response.status}`);
        }
    }
    
    const userInfo = await response.json();
    return userInfo;
} catch (error) {
    console.error('API request failed:', error);
    // Handle network errors etc.
}

FAQ

Q1: Wie erreicht man ein echtes Ein-Klick-Anmeldeerlebnis?

A: Fügen Sie der Autorisierungs-URL einfach den Parameter auto_authorize=true hinzu. Nach der Anmeldung des Nutzers wird die Autorisierung automatisch erteilt – ohne zusätzlichen Bestätigungsschritt:
const authUrl = 'https://your-domain.com/api/oauth2/authorize?' +
  'client_id=YOUR_CLIENT_ID&' +
  'auto_authorize=true&' + // Key parameter
  '...other parameters';

Q2: Werden Tokens automatisch aktualisiert?

A: Ja, in unser SDK ist eine automatische Token-Aktualisierung integriert:
  • Access Token: 2 Stunden gültig, automatische Aktualisierung 5 Minuten vor Ablauf
  • Refresh Token: 30 Tage gültig, wird verwendet, um neue Access Tokens zu erhalten
  • Nahtlose Aktualisierung: Alle API-Aufrufe prüfen automatisch abgelaufene Tokens und aktualisieren sie bei Bedarf
  • Wiederholung bei Fehlern: Gibt die API einen 401-Fehler zurück, wird automatisch versucht, das Token zu aktualisieren und die Anfrage erneut zu senden
// The SDK will handle token refresh automatically, no manual handling needed
const userInfo = await oauthManager.fetchUserInfo(); // token auto-refresh
Nur wenn auch das Refresh Token abgelaufen ist, muss sich der Nutzer erneut anmelden.

Q3: Welche Nutzerinformationen kann ich abrufen?

A: Je nach autorisiertem Scope können Sie Folgendes erhalten:
  • Scope profile: Benutzername, E-Mail
  • Scope balance: Informationen zum Kontostand

Q4: Wie teste ich die OAuth-Integration?

A:
  1. Verwenden Sie in der Entwicklung HTTP localhost zum Testen
  2. Nutzen Sie unsere bereitgestellten Test-Tools, um den Autorisierungsablauf zu verifizieren
  3. Prüfen Sie die Netzwerkanfragen in den Entwicklertools Ihres Browsers

Q5: Welche Programmiersprachen werden unterstützt?

A: Unsere OAuth2-API ist standardkonform und unterstützt alle gängigen Programmiersprachen:
  • JavaScript/Node.js
  • Python
  • PHP
  • Java
  • C#/.NET
  • Go
  • Ruby

Q6: Wie kann ein Nutzer die Autorisierung widerrufen?

A: Nutzer können die Autorisierung von Drittanbieter-Apps in unserem Nutzer-Center > Seite „Autorisierungsverwaltung“ widerrufen.

Zuletzt aktualisiert: 2026-06-01