Skip to main content

Overview

TrailBase supports OAuth 2.0 authentication with popular identity providers. Users can sign in with their existing accounts from Google, GitHub, Microsoft, and other providers.

Supported Providers

TrailBase includes built-in support for:

Google

Google OAuth 2.0

GitHub

GitHub OAuth

Microsoft

Microsoft Azure AD

GitLab

GitLab OAuth

Facebook

Facebook Login

Discord

Discord OAuth 2.0

Twitch

Twitch Authentication

Yandex

Yandex OAuth

Apple

Sign in with Apple

OIDC

Custom OpenID Connect

Configuration

OAuth requires a public URL for redirects. Configure server.site_url in your config:
server {
  site_url: "https://yourdomain.com"
}

Basic Provider Configuration

auth {
  oauth_providers {
    key: "google"
    value {
      provider_id: GOOGLE
      client_id: "your-client-id.apps.googleusercontent.com"
      client_secret: "your-client-secret"
    }
  }
  
  oauth_providers {
    key: "github"
    value {
      provider_id: GITLAB  # Note: Use GITLAB provider_id for GitHub
      client_id: "your-github-oauth-app-id"
      client_secret: "your-github-oauth-app-secret"
    }
  }
}

Provider Setup Guides

Google OAuth

1

Create OAuth Client

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Navigate to “APIs & Services” → “Credentials”
  4. Click “Create Credentials” → “OAuth client ID”
  5. Choose “Web application”
2

Configure Redirect URIs

Add authorized redirect URI:
https://yourdomain.com/api/auth/v1/oauth/google/callback
3

Add to Configuration

auth {
  oauth_providers {
    key: "google"
    value {
      provider_id: GOOGLE
      client_id: "123456789.apps.googleusercontent.com"
      client_secret: "your-client-secret"
    }
  }
}

GitHub OAuth

1

Create OAuth App

  1. Go to GitHub Developer Settings
  2. Click “New OAuth App”
  3. Fill in application details
2

Set Callback URL

https://yourdomain.com/api/auth/v1/oauth/github/callback
3

Configure TrailBase

auth {
  oauth_providers {
    key: "github"
    value {
      provider_id: GITLAB  # Uses GitLab provider internally
      client_id: "your-client-id"
      client_secret: "your-client-secret"
      # GitHub uses GitLab OAuth URL structure
    }
  }
}

Microsoft Azure AD

1

Register Application

  1. Go to Azure Portal
  2. Navigate to “Azure Active Directory” → “App registrations”
  3. Click “New registration”
  4. Name your application
2

Configure Redirect URI

Under “Authentication”, add:
https://yourdomain.com/api/auth/v1/oauth/microsoft/callback
3

Create Client Secret

  1. Go to “Certificates & secrets”
  2. Click “New client secret”
  3. Copy the secret value immediately
4

Add to TrailBase

auth {
  oauth_providers {
    key: "microsoft"
    value {
      provider_id: MICROSOFT
      client_id: "your-application-id"
      client_secret: "your-client-secret"
    }
  }
}

Discord

1

Create Application

  1. Go to Discord Developer Portal
  2. Click “New Application”
  3. Go to “OAuth2” settings
2

Add Redirect

https://yourdomain.com/api/auth/v1/oauth/discord/callback
3

Configure

auth {
  oauth_providers {
    key: "discord"
    value {
      provider_id: DISCORD
      client_id: "your-client-id"
      client_secret: "your-client-secret"
    }
  }
}

GitLab

auth {
  oauth_providers {
    key: "gitlab"
    value {
      provider_id: GITLAB
      client_id: "your-application-id"
      client_secret: "your-secret"
      # Optional: self-hosted GitLab
      # gitlab_url: "https://gitlab.example.com"
    }
  }
}
Redirect URI: https://yourdomain.com/api/auth/v1/oauth/gitlab/callback

Custom OIDC Provider

For providers not explicitly supported, use OpenID Connect:
auth {
  oauth_providers {
    key: "custom_oidc"
    value {
      provider_id: OIDC_0
      client_id: "your-client-id"
      client_secret: "your-client-secret"
      
      # OIDC discovery URL
      oidc_discovery_url: "https://accounts.example.com/.well-known/openid-configuration"
      
      # Or specify endpoints manually:
      # oidc_authorization_url: "https://accounts.example.com/oauth2/auth"
      # oidc_token_url: "https://accounts.example.com/oauth2/token"
      # oidc_userinfo_url: "https://accounts.example.com/oauth2/userinfo"
    }
  }
}

OAuth Flow

TrailBase implements the OAuth 2.0 authorization code flow with PKCE:
┌─────────┐                                         ┌──────────┐
│ Browser │                                         │ Provider │
└────┬────┘                                         └────┬─────┘
     │                                                   │
     │  1. Click "Sign in with Google"                  │
     ├──────────────────────────────────►               │
     │     /api/auth/v1/oauth/google                    │
     │                                                   │
     │  2. Redirect to provider with                    │
     │     state, code_challenge                        │
     ├──────────────────────────────────────────────────►
     │                                                   │
     │  3. User authorizes                              │
     │                                                   │
     │  4. Redirect with auth code                      │
     │◄──────────────────────────────────────────────────┤
     │     /api/auth/v1/oauth/google/callback           │
     │                                                   │
     │  5. Exchange code for token                      │
     │     (with code_verifier)                         │
     ├──────────────────────────────────────────────────►
     │                                                   │
     │  6. Return access token                          │
     │◄──────────────────────────────────────────────────┤
     │                                                   │
     │  7. Fetch user profile                           │
     ├──────────────────────────────────────────────────►
     │                                                   │
     │  8. Return user data                             │
     │◄──────────────────────────────────────────────────┤
     │                                                   │
     │  9. Create/login user, set session cookies       │
     │◄──────────────────────────────────────────        │
     │                                                   │

Security Features

PKCE (Proof Key for Code Exchange)

TrailBase uses PKCE for all OAuth flows:
// From crates/core/src/auth/oauth/state.rs
pub(crate) struct OAuthState {
    /// OAuth CSRF protection
    pub state: String,
    
    /// PKCE code verifier
    pub code_verifier: String,
    
    /// Optional redirect URL after successful auth
    pub redirect_url: Option<String>,
}

State Parameter

Prevents CSRF attacks by validating state:
// State is stored in encrypted JWT cookie
let state = jwt_encode(OAuthState {
    state: random_string(),
    code_verifier: generate_code_verifier(),
    redirect_url: Some("/dashboard".to_string()),
});

User Linking

Users can link multiple OAuth providers to one account:
-- User table tracks OAuth provider associations
CREATE TABLE _user (
  id BLOB PRIMARY KEY,
  email TEXT UNIQUE,
  verified INTEGER NOT NULL DEFAULT 0,
  provider_id INTEGER,  -- OAuth provider ID
  provider_user_id TEXT,  -- User ID from provider
  -- ...
);

API Endpoints

Initiate OAuth Flow

GET /api/auth/v1/oauth/{provider}
Redirects user to provider’s authorization page. Query Parameters:
  • redirect_url (optional): Where to redirect after successful auth
Example:
<a href="/api/auth/v1/oauth/google?redirect_url=/dashboard">
  Sign in with Google
</a>

OAuth Callback

GET /api/auth/v1/oauth/{provider}/callback
Handles the callback from OAuth provider. Automatically processes:
  1. Validates state parameter
  2. Exchanges authorization code for access token
  3. Fetches user profile
  4. Creates or updates user
  5. Sets session cookies
  6. Redirects to redirect_url or default page

List Available Providers

GET /api/auth/v1/oauth/providers
Response:
{
  "providers": [
    {
      "name": "google",
      "display_name": "Google"
    },
    {
      "name": "github",
      "display_name": "GitHub"
    }
  ]
}

Frontend Integration

Sign In Buttons

<!DOCTYPE html>
<html>
<head>
  <title>Sign In</title>
  <style>
    .oauth-button {
      display: inline-flex;
      align-items: center;
      padding: 12px 24px;
      margin: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      text-decoration: none;
      color: #333;
      background: white;
      font-family: system-ui;
    }
    .oauth-button:hover {
      background: #f5f5f5;
    }
    .oauth-button img {
      width: 20px;
      height: 20px;
      margin-right: 12px;
    }
  </style>
</head>
<body>
  <h1>Sign In</h1>
  
  <a href="/api/auth/v1/oauth/google" class="oauth-button">
    <img src="/icons/google.svg" alt="Google">
    Sign in with Google
  </a>
  
  <a href="/api/auth/v1/oauth/github" class="oauth-button">
    <img src="/icons/github.svg" alt="GitHub">
    Sign in with GitHub
  </a>
  
  <a href="/api/auth/v1/oauth/microsoft" class="oauth-button">
    <img src="/icons/microsoft.svg" alt="Microsoft">
    Sign in with Microsoft
  </a>
</body>
</html>

React Component

import React, { useEffect, useState } from 'react';

interface Provider {
  name: string;
  display_name: string;
}

export function OAuthButtons() {
  const [providers, setProviders] = useState<Provider[]>([]);
  
  useEffect(() => {
    fetch('/api/auth/v1/oauth/providers')
      .then(res => res.json())
      .then(data => setProviders(data.providers));
  }, []);
  
  const handleOAuthLogin = (provider: string) => {
    const redirectUrl = encodeURIComponent(window.location.pathname);
    window.location.href = `/api/auth/v1/oauth/${provider}?redirect_url=${redirectUrl}`;
  };
  
  return (
    <div>
      {providers.map(provider => (
        <button
          key={provider.name}
          onClick={() => handleOAuthLogin(provider.name)}
          className="oauth-button"
        >
          Sign in with {provider.display_name}
        </button>
      ))}
    </div>
  );
}

User Profile Data

OAuth providers return different user information:
// From crates/core/src/auth/oauth/mod.rs
pub struct OAuthUser {
    pub provider_user_id: String,  // Provider's user ID
    pub provider_id: OAuthProviderId,
    pub email: Option<String>,
    pub verified: bool,
    pub name: Option<String>,
    pub avatar_url: Option<String>,
}

Admin API

List Available OAuth Providers

curl -H "Authorization: Bearer $ADMIN_TOKEN" \
  https://api.example.com/_/admin/oauth/providers
Response:
{
  "providers": [
    {
      "id": 2,
      "name": "google",
      "display_name": "Google"
    },
    {
      "id": 3,
      "name": "github",
      "display_name": "GitHub"
    }
  ]
}

Provider Implementation

Each provider implements the OAuthProvider trait:
// From crates/core/src/auth/oauth/mod.rs
pub trait OAuthProvider: Send + Sync {
    fn name(&self) -> &str;
    fn provider(&self) -> OAuthProviderId;
    fn settings(&self) -> Result<OAuthClientSettings, AuthError>;
    fn oauth_scopes(&self) -> Vec<&'static str>;
    
    async fn get_user(
        &self,
        token_response: &TokenResponse
    ) -> Result<OAuthUser, AuthError>;
}

Troubleshooting

Error: “redirect_uri_mismatch”Solution: Ensure the callback URL in your provider settings exactly matches:
https://yourdomain.com/api/auth/v1/oauth/{provider}/callback
Check:
  • Protocol (http vs https)
  • Domain name
  • Port (if not 80/443)
  • Path (including /callback)
Error: “OAuth requires a public URL”Solution: Add site_url to your config:
server {
  site_url: "https://yourdomain.com"
}
Error: “invalid_client”Solution:
  • Verify client_id is correct
  • Ensure client_secret hasn’t expired
  • Check provider dashboard for any issues
  • Regenerate credentials if necessary
Error: User data missing (email, name, etc.)Solution: Check OAuth scopes. TrailBase requests:
  • Google: openid email profile
  • GitHub: user:email
  • Discord: identify email
Ensure these scopes are approved in your OAuth app settings.

Best Practices

1

Use HTTPS in production

OAuth requires HTTPS for redirect URIs in production. Use certificates from Let’s Encrypt or your provider.
2

Rotate secrets regularly

Periodically regenerate client secrets and update your configuration.
3

Request minimal scopes

Only request the OAuth scopes your application actually needs.
4

Handle provider outages

Implement fallback authentication methods (email/password) in case OAuth providers are unavailable.
5

Test thoroughly

Test OAuth flows in development before deploying to production.

Next Steps

Email

Configure email for fallback auth

Custom Endpoints

Build custom auth flows

Object Storage

Store user avatars

Jobs Scheduler

Sync OAuth data

Build docs developers (and LLMs) love