> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strikebet.app/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth Callback

> Handle OAuth provider callback and complete authentication

Handle OAuth provider callbacks and complete the authentication flow. This endpoint receives the authorization code from OAuth providers and exchanges it for access tokens to authenticate users.

<Info>
  This endpoint is automatically called by OAuth providers after user authorization. You typically don't call this endpoint directly.
</Info>

<RequestExample>
  ```bash cURL
  # This request is typically made by the OAuth provider
  curl -X GET "http://localhost:8080/callback?code=auth_code_here&state=csrf_state&provider=google"
  ```

  ```javascript JavaScript
  // Handle OAuth callback in your application
  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const code = urlParams.get('code');
    const state = urlParams.get('state');
    const error = urlParams.get('error');

    if (error) {
      console.error('OAuth error:', error);
      return;
    }

    if (code && state) {
      // OAuth callback will be handled automatically by the auth service
      // The user will be redirected to the specified redirect_to URL
      console.log('OAuth callback received, processing...');
    }
  }, []);
  ```

  ```python Python
  # Handle OAuth callback in Flask
  from flask import request, redirect

  @app.route('/oauth/callback')
  def oauth_callback():
      code = request.args.get('code')
      state = request.args.get('state')
      error = request.args.get('error')
      
      if error:
          return f"OAuth error: {error}", 400
      
      if not code:
          return "Missing authorization code", 400
      
      # The auth service will handle the token exchange
      # and redirect the user appropriately
      return redirect('/dashboard')
  ```

  ```go Go
  package main

  import (
      "fmt"
      "net/http"
  )

  func oauthCallback(w http.ResponseWriter, r *http.Request) {
      code := r.URL.Query().Get("code")
      state := r.URL.Query().Get("state")
      errorParam := r.URL.Query().Get("error")
      
      if errorParam != "" {
          http.Error(w, fmt.Sprintf("OAuth error: %s", errorParam), http.StatusBadRequest)
          return
      }
      
      if code == "" {
          http.Error(w, "Missing authorization code", http.StatusBadRequest)
          return
      }
      
      // Auth service handles token exchange and user creation
      http.Redirect(w, r, "/dashboard", http.StatusFound)
  }
  ```
</RequestExample>

## Query Parameters

<ParamField query="code" type="string" required>
  Authorization code returned by the OAuth provider.
</ParamField>

<ParamField query="state" type="string">
  State parameter for CSRF protection (if provided in the authorization request).
</ParamField>

<ParamField query="provider" type="string">
  OAuth provider identifier (may be included in some implementations).
</ParamField>

<ParamField query="error" type="string">
  Error code if the OAuth authorization failed.
</ParamField>

<ParamField query="error_description" type="string">
  Human-readable error description if the OAuth authorization failed.
</ParamField>

## Response

This endpoint typically returns a `302 Found` redirect response to the application's redirect URL with authentication tokens.

<ResponseField name="Location" type="string">
  Redirect URL with authentication tokens or session information
</ResponseField>

<ResponseExample>
  ```http 302 - Successful Authentication
  HTTP/1.1 302 Found
  Location: https://yourapp.com/dashboard?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&expires_in=3600&token_type=bearer
  Set-Cookie: sb-access-token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; HttpOnly; Secure; SameSite=Lax
  Set-Cookie: sb-refresh-token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; HttpOnly; Secure; SameSite=Lax
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```http 302 - OAuth Error
  HTTP/1.1 302 Found
  Location: https://yourapp.com/login?error=access_denied&error_description=User%20denied%20access
  ```

  ```json 400 - Invalid Request
  {
    "code": 400,
    "msg": "Invalid authorization code",
    "details": "The authorization code is invalid or has expired"
  }
  ```

  ```json 400 - State Mismatch
  {
    "code": 400,
    "msg": "State parameter mismatch",
    "details": "The state parameter does not match the expected value"
  }
  ```

  ```json 500 - Provider Error
  {
    "code": 500,
    "msg": "OAuth provider error",
    "details": "Failed to exchange authorization code for access token"
  }
  ```
</ResponseExample>

## OAuth Callback Flow

### Successful Authentication

1. **Authorization Code Exchange**: The auth service exchanges the authorization code for access tokens
2. **User Information Retrieval**: Fetch user profile information from the OAuth provider
3. **User Account Management**: Create or update user account in the database
4. **Session Creation**: Generate authentication tokens for the user
5. **Redirect**: Redirect user to the specified redirect URL with tokens

### Error Handling

Common OAuth errors and their handling:

* **access\_denied**: User denied authorization
* **invalid\_request**: Malformed authorization request
* **unauthorized\_client**: Client not authorized for this grant type
* **unsupported\_response\_type**: Authorization server doesn't support the response type
* **invalid\_scope**: Requested scope is invalid or unknown
* **server\_error**: Authorization server encountered an error
* **temporarily\_unavailable**: Authorization server is temporarily unavailable

## Implementation Examples

### React OAuth Callback Handler

```jsx
import { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';

function OAuthCallback() {
  const [searchParams] = useSearchParams();
  const navigate = useNavigate();
  const [status, setStatus] = useState('processing');
  const [error, setError] = useState(null);

  useEffect(() => {
    handleOAuthCallback();
  }, []);

  const handleOAuthCallback = async () => {
    const code = searchParams.get('code');
    const state = searchParams.get('state');
    const error = searchParams.get('error');
    const errorDescription = searchParams.get('error_description');

    // Handle OAuth errors
    if (error) {
      setStatus('error');
      setError({
        code: error,
        description: errorDescription || 'OAuth authentication failed'
      });
      return;
    }

    // Validate required parameters
    if (!code) {
      setStatus('error');
      setError({
        code: 'missing_code',
        description: 'Authorization code is missing'
      });
      return;
    }

    // Validate state parameter (CSRF protection)
    const storedState = sessionStorage.getItem('oauth_state');
    if (state && storedState && state !== storedState) {
      setStatus('error');
      setError({
        code: 'state_mismatch',
        description: 'State parameter mismatch - possible CSRF attack'
      });
      return;
    }

    try {
      // Extract tokens from URL or cookies
      const accessToken = searchParams.get('access_token');
      const refreshToken = searchParams.get('refresh_token');
      const expiresIn = searchParams.get('expires_in');

      if (accessToken) {
        // Store tokens
        localStorage.setItem('access_token', accessToken);
        if (refreshToken) {
          localStorage.setItem('refresh_token', refreshToken);
        }
        if (expiresIn) {
          const expiresAt = Date.now() + parseInt(expiresIn) * 1000;
          localStorage.setItem('token_expires_at', expiresAt.toString());
        }

        // Clear OAuth state
        sessionStorage.removeItem('oauth_state');

        setStatus('success');
        
        // Redirect to dashboard or intended page
        const redirectTo = sessionStorage.getItem('oauth_redirect_to') || '/dashboard';
        sessionStorage.removeItem('oauth_redirect_to');
        
        setTimeout(() => {
          navigate(redirectTo, { replace: true });
        }, 1000);
      } else {
        // Tokens might be in cookies, check if user is authenticated
        const response = await fetch('/api/user/profile', {
          credentials: 'include'
        });

        if (response.ok) {
          setStatus('success');
          const redirectTo = sessionStorage.getItem('oauth_redirect_to') || '/dashboard';
          sessionStorage.removeItem('oauth_redirect_to');
          navigate(redirectTo, { replace: true });
        } else {
          throw new Error('Authentication failed');
        }
      }
    } catch (err) {
      setStatus('error');
      setError({
        code: 'auth_failed',
        description: err.message || 'Authentication failed'
      });
    }
  };

  if (status === 'processing') {
    return (
      <div className="oauth-callback-processing">
        <div className="spinner"></div>
        <h2>Completing authentication...</h2>
        <p>Please wait while we finish setting up your account.</p>
      </div>
    );
  }

  if (status === 'success') {
    return (
      <div className="oauth-callback-success">
        <div className="success-icon">✅</div>
        <h2>Authentication successful!</h2>
        <p>Redirecting you to your dashboard...</p>
      </div>
    );
  }

  if (status === 'error') {
    return (
      <div className="oauth-callback-error">
        <div className="error-icon">❌</div>
        <h2>Authentication failed</h2>
        <p>{error.description}</p>
        <div className="error-actions">
          <button onClick={() => navigate('/login')}>
            Try Again
          </button>
          <button onClick={() => navigate('/support')}>
            Get Help
          </button>
        </div>
      </div>
    );
  }

  return null;
}

export default OAuthCallback;
```

### OAuth Token Extraction Hook

```jsx
import { useEffect, useState } from 'react';

function useOAuthTokens() {
  const [tokens, setTokens] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    extractTokensFromCallback();
  }, []);

  const extractTokensFromCallback = () => {
    try {
      // Check URL parameters first
      const urlParams = new URLSearchParams(window.location.search);
      const accessToken = urlParams.get('access_token');
      const refreshToken = urlParams.get('refresh_token');
      const expiresIn = urlParams.get('expires_in');

      if (accessToken) {
        const tokenData = {
          access_token: accessToken,
          refresh_token: refreshToken,
          expires_in: expiresIn ? parseInt(expiresIn) : null,
          expires_at: expiresIn ? Date.now() + parseInt(expiresIn) * 1000 : null
        };

        setTokens(tokenData);
        
        // Store tokens securely
        localStorage.setItem('auth_tokens', JSON.stringify(tokenData));
        
        // Clean URL
        window.history.replaceState({}, document.title, window.location.pathname);
      } else {
        // Check if tokens are in localStorage
        const storedTokens = localStorage.getItem('auth_tokens');
        if (storedTokens) {
          setTokens(JSON.parse(storedTokens));
        }
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const clearTokens = () => {
    setTokens(null);
    localStorage.removeItem('auth_tokens');
  };

  const isTokenExpired = () => {
    if (!tokens?.expires_at) return false;
    return Date.now() >= tokens.expires_at;
  };

  return {
    tokens,
    loading,
    error,
    clearTokens,
    isTokenExpired
  };
}

export default useOAuthTokens;
```

### Node.js OAuth Callback Handler

```javascript
const express = require('express');
const { query, validationResult } = require('express-validator');

const router = express.Router();

router.get('/callback', [
  query('code').optional().isString(),
  query('state').optional().isString(),
  query('error').optional().isString(),
  query('provider').optional().isString()
], async (req, res) => {
  try {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        code: 400,
        msg: 'Invalid callback parameters',
        details: errors.array()
      });
    }

    const { code, state, error, error_description, provider } = req.query;

    // Handle OAuth errors
    if (error) {
      const errorUrl = `${process.env.APP_URL}/login?error=${error}&error_description=${encodeURIComponent(error_description || '')}`;
      return res.redirect(errorUrl);
    }

    // Validate authorization code
    if (!code) {
      return res.status(400).json({
        code: 400,
        msg: 'Missing authorization code',
        details: 'Authorization code is required for OAuth callback'
      });
    }

    // Validate state parameter (if provided)
    if (state) {
      const storedState = req.session?.oauth_state;
      if (!storedState || storedState !== state) {
        return res.status(400).json({
          code: 400,
          msg: 'State parameter mismatch',
          details: 'Invalid state parameter - possible CSRF attack'
        });
      }
    }

    // Forward to Strike Auth Service for token exchange
    const callbackUrl = `${process.env.AUTH_SERVICE_URL}/callback?${req.url.split('?')[1]}`;
    
    const response = await fetch(callbackUrl, {
      method: 'GET',
      headers: {
        'User-Agent': req.headers['user-agent'],
        'X-Forwarded-For': req.ip,
        'X-Real-IP': req.ip
      },
      redirect: 'manual'
    });

    // Handle redirect response
    if (response.status === 302) {
      const location = response.headers.get('location');
      
      // Extract and set cookies if present
      const cookies = response.headers.get('set-cookie');
      if (cookies) {
        res.set('Set-Cookie', cookies);
      }
      
      return res.redirect(location);
    }

    // Handle error response
    if (!response.ok) {
      const errorData = await response.json();
      return res.status(response.status).json(errorData);
    }

    // This shouldn't happen in normal flow
    const data = await response.json();
    res.json(data);

  } catch (error) {
    console.error('OAuth callback error:', error);
    
    // Redirect to error page
    const errorUrl = `${process.env.APP_URL}/login?error=server_error&error_description=${encodeURIComponent('Authentication failed')}`;
    res.redirect(errorUrl);
  }
});

module.exports = router;
```

### OAuth Error Handler Component

```jsx
import { useSearchParams, useNavigate } from 'react-router-dom';

function OAuthError() {
  const [searchParams] = useSearchParams();
  const navigate = useNavigate();
  
  const error = searchParams.get('error');
  const errorDescription = searchParams.get('error_description');

  const getErrorMessage = (errorCode) => {
    const errorMessages = {
      'access_denied': 'You denied access to your account. Please try again if you want to sign in.',
      'invalid_request': 'The authentication request was invalid. Please try again.',
      'unauthorized_client': 'This application is not authorized to use this authentication method.',
      'unsupported_response_type': 'The authentication method is not supported.',
      'invalid_scope': 'The requested permissions are invalid.',
      'server_error': 'The authentication server encountered an error. Please try again.',
      'temporarily_unavailable': 'The authentication service is temporarily unavailable. Please try again later.',
      'state_mismatch': 'Security validation failed. Please try again.',
      'auth_failed': 'Authentication failed. Please try again.'
    };

    return errorMessages[errorCode] || 'An unknown error occurred during authentication.';
  };

  const getErrorSolution = (errorCode) => {
    const solutions = {
      'access_denied': 'Click "Try Again" to restart the authentication process.',
      'server_error': 'Wait a moment and try again. If the problem persists, contact support.',
      'temporarily_unavailable': 'Please wait a few minutes and try again.',
      'state_mismatch': 'Clear your browser cache and try again.',
      'auth_failed': 'Check your internet connection and try again.'
    };

    return solutions[errorCode] || 'Please try again or contact support if the problem persists.';
  };

  return (
    <div className="oauth-error-page">
      <div className="error-container">
        <div className="error-icon">⚠️</div>
        
        <h1>Authentication Failed</h1>
        
        <div className="error-details">
          <p className="error-message">
            {getErrorMessage(error)}
          </p>
          
          {errorDescription && (
            <p className="error-description">
              {decodeURIComponent(errorDescription)}
            </p>
          )}
          
          <p className="error-solution">
            {getErrorSolution(error)}
          </p>
        </div>

        <div className="error-actions">
          <button 
            onClick={() => navigate('/login')}
            className="primary-button"
          >
            Try Again
          </button>
          
          <button 
            onClick={() => navigate('/signup')}
            className="secondary-button"
          >
            Create Account
          </button>
          
          <button 
            onClick={() => navigate('/support')}
            className="text-button"
          >
            Contact Support
          </button>
        </div>

        <div className="error-code">
          Error Code: {error || 'unknown'}
        </div>
      </div>
    </div>
  );
}

export default OAuthError;
```

## Security Considerations

* **State Parameter Validation**: Always validate the state parameter to prevent CSRF attacks
* **Authorization Code Expiration**: Authorization codes are short-lived (typically 10 minutes)
* **Token Security**: Store tokens securely and use HttpOnly cookies when possible
* **Redirect URL Validation**: Ensure redirect URLs are whitelisted
* **Error Handling**: Don't expose sensitive error information to users

## Callback URL Configuration

### Development

```
http://localhost:8080/callback
```

### Production

```
https://yourapp.com/auth/callback
```

### Multiple Environments

```javascript
const getCallbackURL = () => {
  const baseURL = process.env.NODE_ENV === 'production' 
    ? 'https://yourapp.com' 
    : 'http://localhost:3000';
  
  return `${baseURL}/auth/callback`;
};
```

## Best Practices

<AccordionGroup>
  <Accordion title="Security">
    * Always validate state parameters
    * Use HTTPS in production
    * Implement proper error handling
    * Store tokens securely
    * Set appropriate cookie flags
  </Accordion>

  <Accordion title="User Experience">
    * Show loading states during processing
    * Provide clear error messages
    * Offer alternative authentication methods
    * Handle network failures gracefully
    * Clean URLs after token extraction
  </Accordion>

  <Accordion title="Error Handling">
    * Log OAuth errors for debugging
    * Provide user-friendly error messages
    * Implement retry mechanisms
    * Handle expired authorization codes
    * Gracefully handle provider downtime
  </Accordion>
</AccordionGroup>

## Testing

### Unit Tests

```javascript
describe('GET /callback', () => {
  test('should handle successful OAuth callback', async () => {
    const response = await request(app)
      .get('/callback')
      .query({
        code: 'valid_auth_code',
        state: 'valid_state'
      })
      .expect(302);

    expect(response.headers.location).toContain('/dashboard');
  });

  test('should handle OAuth error', async () => {
    const response = await request(app)
      .get('/callback')
      .query({
        error: 'access_denied',
        error_description: 'User denied access'
      })
      .expect(302);

    expect(response.headers.location).toContain('error=access_denied');
  });

  test('should reject missing authorization code', async () => {
    await request(app)
      .get('/callback')
      .query({
        state: 'valid_state'
      })
      .expect(400);
  });
});
```

## Related Endpoints

<CardGroup cols={2}>
  <Card title="OAuth Authorize" icon="arrow-up-right-from-square" href="/api-reference/oauth/authorize">
    Initiate OAuth authorization flow
  </Card>

  <Card title="Login" icon="right-to-bracket" href="/api-reference/authentication/login">
    Traditional email/password login
  </Card>

  <Card title="Get Profile" icon="user" href="/api-reference/user/get-profile">
    Get authenticated user profile
  </Card>

  <Card title="Logout" icon="right-from-bracket" href="/api-reference/user/logout">
    End user sessions
  </Card>
</CardGroup>
