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:
§ Installation
Install the Socialite plugin using the CLI:
$ nimbus plugin:install socialite
This will:
- Add the
socialiteimport tobin/server.go - Register the plugin with
app.Use(socialite.NewPlugin(...)) - Scaffold
config/socialite.gowith provider configuration - 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
- Scopes:
user:email - Fetches user profile + private emails from
/user/emails - Creates app: GitHub Developer Settings
- Scopes:
openid email profile - Uses Google's userinfo endpoint with OAuth2 token exchange
- Configure at Google Cloud Console
Discord
- Scopes:
identify email - Constructs avatar URL from Discord CDN
- Create bot at Discord Developer Portal
Apple Sign-In
- Scopes:
name email - Uses
form_postresponse 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
stateparameter 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.