Skip to content

OAuth Authentication

Third-party OAuth authentication supporting Google, GitHub, and other providers.

How It Works

OAuth provides single sign-on (SSO) capabilities:

  1. User clicks "Sign in with Provider"
  2. Redirects to provider's authorization page
  3. User authorizes application
  4. Provider redirects back with authorization code
  5. Exchange code for De. access token

Supported Providers: Google, GitHub, Microsoft, Apple

OAuth Flow

1

Initiate OAuth

Redirect user to provider

typescript
// Generate authorization URL
GET https://auth.dedot.io/v1/auth/oauth/{provider}/authorize?redirect_uri={your_redirect_uri}

// Example for Google
https://auth.dedot.io/v1/auth/oauth/google/authorize?redirect_uri=https://yourapp.com/auth/callback

Response: HTTP 302 redirect to provider's authorization page

2

Handle Callback

Exchange code for token

After user authorizes, provider redirects to your redirect_uri with authorization code:

https://yourapp.com/auth/callback?code=AUTH_CODE&state=STATE

Exchange the code for De. access token:

typescript
POST https://auth.dedot.io/v1/auth/oauth/{provider}/callback

{
  "code": "AUTH_CODE",
  "redirectUri": "https://yourapp.com/auth/callback"
}

Response:

json
{
  "success": true,
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "refresh_token_here",
  "expiresIn": 3600,
  "user": {
    "id": "usr_123456",
    "email": "[email protected]",
    "name": "John Doe",
    "avatar": "https://lh3.googleusercontent.com/...",
    "provider": "google",
    "providerId": "google_user_id_123",
    "verified": true
  }
}

Complete Implementation

React OAuth Component

typescript
import { useEffect, useState } from 'react'

function OAuthLogin() {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState('')

  // Handle OAuth callback
  useEffect(() => {
    const handleCallback = async () => {
      const urlParams = new URLSearchParams(window.location.search)
      const code = urlParams.get('code')
      const provider = urlParams.get('provider')
      
      if (code && provider) {
        setLoading(true)
        
        try {
          const response = await fetch(
            `https://auth.dedot.io/v1/auth/oauth/${provider}/callback`,
            {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({
                code,
                redirectUri: window.location.origin + '/auth/callback'
              })
            }
          )
          
          const data = await response.json()
          
          if (data.success) {
            // Store tokens
            localStorage.setItem('accessToken', data.accessToken)
            localStorage.setItem('refreshToken', data.refreshToken)
            
            // Redirect to app
            window.location.href = '/dashboard'
          } else {
            setError(data.message || 'OAuth authentication failed')
          }
        } catch (err) {
          setError('Network error. Please try again.')
        } finally {
          setLoading(false)
        }
      }
    }
    
    handleCallback()
  }, [])

  const initiateOAuth = (provider: 'google' | 'github' | 'microsoft' | 'apple') => {
    const redirectUri = encodeURIComponent(
      window.location.origin + '/auth/callback'
    )
    
    window.location.href = `https://auth.dedot.io/v1/auth/oauth/${provider}/authorize?redirect_uri=${redirectUri}`
  }

  if (loading) {
    return <div>Authenticating...</div>
  }

  return (
    <div className="oauth-buttons">
      <button onClick={() => initiateOAuth('google')}>
        <img src="/google-icon.svg" alt="Google" />
        Continue with Google
      </button>
      
      <button onClick={() => initiateOAuth('github')}>
        <img src="/github-icon.svg" alt="GitHub" />
        Continue with GitHub
      </button>
      
      <button onClick={() => initiateOAuth('microsoft')}>
        <img src="/microsoft-icon.svg" alt="Microsoft" />
        Continue with Microsoft
      </button>
      
      <button onClick={() => initiateOAuth('apple')}>
        <img src="/apple-icon.svg" alt="Apple" />
        Continue with Apple
      </button>
      
      {error && <div className="error">{error}</div>}
    </div>
  )
}

Next.js OAuth Implementation

typescript
// app/auth/[provider]/route.ts
import { NextResponse } from 'next/server'

export async function GET(
  request: Request,
  { params }: { params: { provider: string } }
) {
  const { searchParams } = new URL(request.url)
  const redirectUri = searchParams.get('redirect_uri') || 
    `${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`
  
  const authUrl = `https://auth.dedot.io/v1/auth/oauth/${params.provider}/authorize?redirect_uri=${encodeURIComponent(redirectUri)}`
  
  return NextResponse.redirect(authUrl)
}

// app/auth/callback/page.tsx
'use client'

import { useEffect, useState } from 'react'
import { useSearchParams, useRouter } from 'next/navigation'

export default function OAuthCallback() {
  const searchParams = useSearchParams()
  const router = useRouter()
  const [error, setError] = useState('')

  useEffect(() => {
    const handleCallback = async () => {
      const code = searchParams.get('code')
      const provider = searchParams.get('provider')
      
      if (!code || !provider) {
        setError('Invalid OAuth callback')
        return
      }
      
      try {
        const response = await fetch(
          `https://auth.dedot.io/v1/auth/oauth/${provider}/callback`,
          {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              code,
              redirectUri: window.location.origin + '/auth/callback'
            })
          }
        )
        
        const data = await response.json()
        
        if (data.success) {
          // Store tokens in httpOnly cookies
          document.cookie = `accessToken=${data.accessToken}; path=/; secure; samesite=strict`
          
          router.push('/dashboard')
        } else {
          setError(data.message || 'Authentication failed')
        }
      } catch (err) {
        setError('Network error')
      }
    }
    
    handleCallback()
  }, [searchParams, router])

  if (error) {
    return <div>Error: {error}</div>
  }

  return <div>Completing authentication...</div>
}

Provider Configuration

Google OAuth

Setup:

  1. Create OAuth 2.0 Client in Google Cloud Console
  2. Add authorized redirect URI: https://auth.dedot.io/v1/auth/oauth/google/callback
  3. Configure in De. dashboard with Client ID and Secret

Scopes Requested:

  • openid
  • email
  • profile

GitHub OAuth

Setup:

  1. Create OAuth App in GitHub Settings
  2. Set authorization callback URL: https://auth.dedot.io/v1/auth/oauth/github/callback
  3. Configure in De. dashboard

Scopes Requested:

  • user:email
  • read:user

Microsoft OAuth

Setup:

  1. Register app in Azure Portal
  2. Add redirect URI: https://auth.dedot.io/v1/auth/oauth/microsoft/callback
  3. Configure in De. dashboard

Scopes Requested:

  • openid
  • email
  • profile

Apple Sign In

Setup:

  1. Configure in Apple Developer Portal
  2. Add return URL: https://auth.dedot.io/v1/auth/oauth/apple/callback
  3. Configure in De. dashboard

Scopes Requested:

  • email
  • name

Account Linking

Link OAuth provider to existing account:

typescript
POST https://auth.dedot.io/v1/auth/oauth/link

Headers:
  Authorization: Bearer <access_token>

Body:
{
  "provider": "google",
  "code": "AUTH_CODE",
  "redirectUri": "https://yourapp.com/auth/callback"
}

Response:

json
{
  "success": true,
  "message": "Google account linked successfully",
  "linkedProviders": ["email", "google"]
}
typescript
DELETE https://auth.dedot.io/v1/auth/oauth/unlink/{provider}

Headers:
  Authorization: Bearer <access_token>

Response:

json
{
  "success": true,
  "message": "GitHub account unlinked",
  "remainingProviders": ["email", "google"]
}

Security Considerations

Best Practices

  • Always use state parameter to prevent CSRF
  • Validate redirect_uri matches registered URIs
  • Store tokens securely (httpOnly cookies)
  • Implement PKCE for mobile apps
  • Use short-lived authorization codes
  • Verify email from OAuth provider

Important Notes

  • OAuth tokens are provider-specific
  • Email may not be verified by provider
  • Not all providers return email
  • Handle provider API rate limits
  • Implement fallback authentication
  • Test with multiple providers

Error Handling

typescript
async function handleOAuthCallback(code: string, provider: string) {
  try {
    const response = await fetch(
      `https://auth.dedot.io/v1/auth/oauth/${provider}/callback`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          code,
          redirectUri: window.location.origin + '/auth/callback'
        })
      }
    )
    
    const data = await response.json()
    
    if (!response.ok) {
      switch (response.status) {
        case 400:
          throw new Error('Invalid authorization code')
        case 401:
          throw new Error('Provider authentication failed')
        case 409:
          throw new Error('Account already exists with different provider')
        case 429:
          throw new Error('Too many requests. Please try again later.')
        default:
          throw new Error(data.message || 'OAuth authentication failed')
      }
    }
    
    return data
  } catch (error) {
    console.error('OAuth error:', error)
    throw error
  }
}

Testing

Development OAuth test accounts:

ProviderEmailBehavior
google[email protected]Always succeeds
github[email protected]Always succeeds
microsoft[email protected]Always succeeds

Development Only

Test OAuth accounts only work in development environment.

Next Steps