Plugin Auth

Socialite — OAuth Authentication

Social authentication for Nimbus. Sign in with GitHub, Google, Discord, Apple — and more — using a clean, unified API inspired by Laravel Socialite.

§ Overview

Socialite handles the entire OAuth2 flow — redirect, callback, user profile retrieval — so you can add social login with just a few lines of code. It ships as a first-party Nimbus plugin with built-in support for:

GitHub
Google
Discord
Apple

§ Installation

Install the Socialite plugin using the CLI:

$ nimbus plugin:install socialite

This will:

  1. Add the socialite import to bin/server.go
  2. Register the plugin with app.Use(socialite.NewPlugin(...))
  3. Scaffold config/socialite.go with provider configuration
  4. Add required environment variables to .env

Or install manually:

$ go get github.com/CodeSyncr/nimbus/auth/socialite

§ Configuration

After installation, configure your OAuth providers in config/socialite.go:

package config

import (
    "os"
    "github.com/CodeSyncr/nimbus/auth/socialite"
)

// SocialiteProviders returns the configured OAuth providers.
func SocialiteProviders() map[string]socialite.ProviderConfig {
    providers := make(map[string]socialite.ProviderConfig)

    if id := os.Getenv("GITHUB_CLIENT_ID"); id != "" {
        providers["github"] = socialite.ProviderConfig{
            ClientID:     id,
            ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
            RedirectURL:  os.Getenv("GITHUB_REDIRECT_URL"),
        }
    }

    if id := os.Getenv("GOOGLE_CLIENT_ID"); id != "" {
        providers["google"] = socialite.ProviderConfig{
            ClientID:     id,
            ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
            RedirectURL:  os.Getenv("GOOGLE_REDIRECT_URL"),
        }
    }

    if id := os.Getenv("DISCORD_CLIENT_ID"); id != "" {
        providers["discord"] = socialite.ProviderConfig{
            ClientID:     id,
            ClientSecret: os.Getenv("DISCORD_CLIENT_SECRET"),
            RedirectURL:  os.Getenv("DISCORD_REDIRECT_URL"),
        }
    }

    if id := os.Getenv("APPLE_CLIENT_ID"); id != "" {
        providers["apple"] = socialite.ProviderConfig{
            ClientID:     id,
            ClientSecret: os.Getenv("APPLE_CLIENT_SECRET"),
            RedirectURL:  os.Getenv("APPLE_REDIRECT_URL"),
        }
    }

    return providers
}

Add the required environment variables to your .env:

# GitHub OAuth
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
GITHUB_REDIRECT_URL=http://localhost:3000/auth/github/callback

# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_REDIRECT_URL=http://localhost:3000/auth/google/callback

# Discord OAuth
DISCORD_CLIENT_ID=your_discord_client_id
DISCORD_CLIENT_SECRET=your_discord_client_secret
DISCORD_REDIRECT_URL=http://localhost:3000/auth/discord/callback

# Apple Sign-In
APPLE_CLIENT_ID=your_apple_service_id
APPLE_CLIENT_SECRET=your_apple_secret_key
APPLE_REDIRECT_URL=http://localhost:3000/auth/apple/callback

§ Registering the Plugin

Register Socialite in your bin/server.go with a callback that handles the authenticated user:

import "github.com/CodeSyncr/nimbus/auth/socialite"

app.Use(socialite.NewPlugin(socialite.Config{
    Providers: config.SocialiteProviders(),
}, func(c *nhttp.Context, user *socialite.SocialUser) error {
    // user.Provider  → "github", "google", etc.
    // user.ID        → Provider user ID
    // user.Name      → Display name
    // user.Email     → Email address
    // user.Avatar    → Avatar URL
    // user.AccessToken → OAuth access token

    // Find or create the user in your database
    // Set session, redirect...
    return c.Redirect("/dashboard")
}))

§ Auto-Registered Routes

Socialite automatically registers two routes per provider:

Method Route Description
GET /auth/{provider} Redirects to OAuth provider
GET /auth/{provider}/callback Handles OAuth callback

For example, with GitHub configured, your login button links to:

<a href="/auth/github">Sign in with GitHub</a>

§ The SocialUser Object

When the callback succeeds, your handler receives a *socialite.SocialUser:

type SocialUser struct {
    Provider    string         // "github", "google", "discord", "apple"
    ID          string         // Unique user ID from the provider
    Name        string         // Display name
    Email       string         // Email address
    Avatar      string         // Avatar / profile picture URL
    AccessToken string         // OAuth access token (for API calls)
    ExpiresAt   time.Time      // Token expiration (if available)
    Raw         map[string]any // Full raw response from provider
}

§ Provider Details

GitHub

Google

  • Scopes: openid email profile
  • Uses Google's userinfo endpoint with OAuth2 token exchange
  • Configure at Google Cloud Console

Discord

Apple Sign-In

  • Scopes: name email
  • Uses form_post response mode + JWT ID token decoding
  • Apple only sends user name on the first authorization — persist it immediately
  • Configure at Apple Developer

§ Complete Example

A full example using Socialite with a user model:

app.Use(socialite.NewPlugin(socialite.Config{
    Providers: config.SocialiteProviders(),
}, func(c *nhttp.Context, user *socialite.SocialUser) error {
    // Find existing user by provider + ID
    var dbUser models.User
    result := db.Where("provider = ? AND provider_id = ?",
        user.Provider, user.ID).First(&dbUser)

    if result.Error != nil {
        // Create new user
        dbUser = models.User{
            Name:       user.Name,
            Email:      user.Email,
            Avatar:     user.Avatar,
            Provider:   user.Provider,
            ProviderID: user.ID,
        }
        db.Create(&dbUser)
    }

    // Set session
    c.Session().Put("user_id", dbUser.ID)
    c.Session().Put("user_name", dbUser.Name)

    return c.Redirect("/dashboard")
}))

§ Security Notes

  • CSRF State: Socialite automatically generates and validates a random state parameter to prevent cross-site request forgery attacks
  • Session storage: The state is stored in the user's session and verified on callback
  • HTTPS required: Always use HTTPS redirect URLs in production
  • Secret rotation: Never commit OAuth secrets — use environment variables

Tip: You can extend Socialite with custom providers by implementing the Provider interface with Name(), AuthURL(state string), and Exchange(code string) methods.