Authula
Plugins

OAuth2 Plugin

Enable OAuth2 authentication for your applications with ease.

Overview

The OAuth2 plugin for Authula allows you to integrate third-party OAuth2 providers into your authentication flow, enabling users to sign in using their accounts from popular services like Google, GitHub, and Discord. Custom OAuth2/OIDC providers can also be configured with their own endpoints and field mappings.

Features

  • Third-Party Authentication — Allow users to authenticate using existing accounts from OAuth2-enabled providers
  • Multiple Provider Support — Configure and enable multiple built-in or custom OAuth2 providers simultaneously
  • Automatic Token Management — Handles authorization requests, token exchanges, and user profile retrieval
  • PKCE Protection — Uses S256 PKCE for Discord, GitHub, Google, and custom providers
  • Account Linking — Links provider accounts to existing users safely, with verified-email checks for anonymous requests
  • Security Best Practices — Built-in support for secure credential management via environment variables

Supported Providers

  • Discord — Enable sign-in with Discord accounts
  • GitHub — Allow users to authenticate using their GitHub credentials
  • Google — Integrate Google OAuth2 for seamless user authentication
  • Custom OAuth2/OIDC — Connect any provider that supports the authorization code flow

Each built-in provider guide includes step-by-step instructions for obtaining credentials, configuring the provider, and testing the integration.


Configuration

Standalone Mode

[plugins.oauth2]
enabled = true
http_timeout = "10s"

# SECURITY NOTE: It is recommended to set the 'client_secret' for each provider via their
# respective environment variables as shown in the .env.example file rather than hardcoding it here.
[plugins.oauth2.providers.google]
enabled = true
client_id = "your-client-id"
client_secret = "your-client-secret"
redirect_url = "http://localhost:8080/auth/oauth2/callback/google"
scopes = []

Library Mode

import (
  "os"

  oauth2plugin "github.com/Authula/authula/plugins/oauth2"
  oauth2plugintypes "github.com/Authula/authula/plugins/oauth2/types"
)

oauth2plugin.New(oauth2plugintypes.OAuth2PluginConfig{
  Enabled: true,
  Providers: map[string]oauth2plugintypes.ProviderConfig{
    "google": {
      Enabled:      true,
      ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
      ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
      RedirectURL:  "https://yourdomain.com/auth/oauth2/callback/google",
    },
  },
})

Configuration Options

OptionTypeDefaultDescription
enabledbool—Enable the OAuth2 plugin
http_timeouttime.Duration—Timeout for token and user-info requests. Responses are capped at 1 MiB

Provider Options

OptionTypeDefaultDescription
enabledbooltrueEnable this provider
client_idstring—OAuth2 client ID
client_secretstring—OAuth2 client secret
redirect_urlstring<base_url><base_path>/oauth2/callback/<provider>Callback URL registered with the provider
scopes[]stringBuilt-in provider scopesScopes requested during authorization
auth_urlstringBuilt-in provider endpointAuthorization endpoint. Required for custom providers
token_urlstringBuilt-in provider endpointToken endpoint. Required for custom providers
user_info_urlstringBuilt-in provider endpointUser-info endpoint. Required for custom providers
user_id_fieldstringProvider-specificField containing the provider account ID
email_fieldstringProvider-specificField containing the user's email
name_fieldstringProvider-specificField containing the user's display name
picture_fieldstringProvider-specificField containing the user's profile picture
email_verified_fieldstringProvider-specificField indicating whether the provider has verified the user's email

Built-in scopes and endpoints are used when the config omits them.

Custom Providers

Custom providers are registered when auth_url, token_url, and user_info_url are all set. Endpoints must use HTTPS, except when connecting to localhost.

[plugins.oauth2.providers.okta]
client_id = "your-client-id"
client_secret = "your-client-secret"
redirect_url = "https://api.example.com/auth/oauth2/callback/okta"
auth_url = "https://example.okta.com/oauth2/v1/authorize"
token_url = "https://example.okta.com/oauth2/v1/token"
user_info_url = "https://example.okta.com/oauth2/v1/userinfo"
scopes = ["openid", "email", "profile"]
user_id_field = "sub"
email_field = "email"
name_field = "name"
picture_field = "picture"
email_verified_field = "email_verified"

Environment Variables

Google, GitHub, and Discord also read credentials from the following environment variables:

  • GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET
  • GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET
  • DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRET

Environment values override only client_id and client_secret; other configuration fields are kept. An explicit redirect_url always wins. Setting only one variable of a pair is an error at startup.

Route Mappings

Register the OAuth2 routes so the session plugin can resolve an existing user during account linking:

[[route_mappings]]
paths = [
  "GET:/oauth2/authorize/{provider}",
  "GET:/oauth2/callback/{provider}",
]
plugins = ["session.auth.optional"]

The plugin relies on the core User, Account, Session, and Token services. The session plugin is required to issue the session cookie; the OAuth2 plugin sets the actor and session values but does not write the cookie itself.


API Reference

HTTP MethodRoute PathDescription
GET/oauth2/authorize/{provider}Initiate OAuth2 authorization flow
GET/oauth2/callback/{provider}Handle OAuth2 callback and token exchange

Account Linking

The provider account identity is authoritative:

  1. The callback resolves the account by (provider, provider_account_id) first.
  2. If the account exists, the callback signs in the account owner. The provider email is never used to switch users.
  3. If the account does not exist and the request is authenticated, the account is linked to the signed-in user.
  4. If the account does not exist and the request is anonymous, an existing local user is linked only when the provider asserts the email is verified.
  5. An unverified provider email never auto-links. If a local user already owns that email, the callback returns an error instead of signing in.

A provider account owned by another user returns 409 Conflict. A user can have multiple provider accounts, and each (provider_id, account_id) pair is unique.

PKCE and Flow Cookies

Discord, GitHub, Google, and custom providers use S256 PKCE. The authorize step returns a signed verifier cookie and includes code_challenge in the authorization URL. The callback requires the verifier and passes it to the token exchange.

The plugin sets three short-lived flow cookies:

CookiePurpose
oauth2_stateSigned state bound to the provider and flow purpose
oauth2_redirect_toSigned redirect destination
oauth2_verifierSigned PKCE verifier

All flow cookies are HttpOnly, use Path=/, and expire after five minutes. Secure, Domain, and SameSite come from the session configuration, not from X-Forwarded-Proto. Cookies are cleared on every terminal callback path, including errors and already-authenticated links.

Redirects

redirect_to accepts relative paths and absolute URLs on trusted origins (security.trusted_origins). Protocol-relative URLs, non-HTTP(S) schemes, credentials, and untrusted hosts are rejected. The signed value is validated again before the Location header is set.

Responses

The callback returns the user and a safe session view. It never returns the session token, IP address, or user agent. The session cookie is the supported credential.

StatusMeaning
401Provider denied access, or authentication required
404Unknown provider
409Account conflict or account linked to another user
422Invalid state, code, PKCE verifier, email, or provider account ID
502Provider request failed
500Internal error

Database Schema

This plugin doesn't create any database tables.


Plugin Capabilities

This plugin doesn't have any hooks and capabilities.


Security Recommendations

  • Use Environment Variables for Secrets — Never hardcode client_secret values in your TOML configuration. Use environment variables as shown in the .env.example file.
  • Secure Redirect URLs — Ensure redirect URLs use HTTPS in production to prevent token interception.
  • Validate Provider Scopes — Only request the minimum scopes necessary for your application to reduce security exposure.
  • Restrict Redirect Destinations — Add trusted frontend origins to security.trusted_origins, and never accept arbitrary redirect targets from untrusted input.
  • Use Secure Cookies — Set secure = true and an appropriate SameSite value in the session configuration in production.
  • Rely on Verified Emails — Only auto-link provider accounts to existing users when the provider asserts the email is verified, or require the user to sign in first.

Client Plugin

If you're using the Authula SDK, add the plugin to the SDK like so:

import { createClient } from "authula";
import { OAuth2Plugin } from "authula/plugins";

export const authulaClient = createClient({
  url: "http://localhost:8080/auth",
  plugins: [
    // other plugins...
    new OAuth2Plugin(),
  ],
});

On this page