oauthgo

module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2025 License: MIT

README

oauthgo

A lightweight and extensible Go library for OAuth2 and OpenID Connect (OIDC), designed for clarity, modularity, and production-grade security. oauthgo simplifies authentication flows with built-in support for major identity providers, plug-and-play session and cookie stores, and robust defenses including PKCE, state/nonce validation, and replay protection — all while remaining unopinionated and compatible with any HTTP router or framework.

Repository: github.com/zekith/oauthgo

Table of Contents


Why oauthgo?

oauthgo streamlines OAuth2 / OIDC flows without locking you into a specific web framework or storage engine.

  • First‑class OIDC (Authorization Code + PKCE) with fallback to OAuth2‑only for providers that don’t implement OIDC.
  • Provider presets (e.g., Google, Microsoft Entra ID, LinkedIn, Facebook, GitHub, SAP Concur, etc.) you can use as‑is or customize.
  • Add a new provider Just register provider endpoints and scopes with core — no need to fork or modify the library.
  • Callback Flexibility: Configure callback options and result backed by a solid foundation.
  • Pluggable sessions (in‑memory or Redis) that you can swap without touching handler wiring.
  • Pluggable cookie manager that you can swap without touching handler wiring.
  • Defense‑in‑depth: state/nonce validation, PKCE, replay protection, and strict redirect handling.
  • Unopinionated routing: plain net/http handlers, compatible with any mux or framework like gin.

The repository currently contains api/, core/, demo/, and provider/ packages at the top level.


Features

  • Authorization Code + PKCE (recommended for web/public clients)
  • State & nonce generation and verification
  • UserInfo retrieval for OIDC and OAuth2‑only providers (via REST) when applicable
  • Session storage abstractions (swap memory ↔ Redis in seconds)
  • Supporting 50+ providers out-of-box
  • Small, focused surface area; easy to extend for new providers

Installation

go get github.com/zekith/oauthgo

Optionally, add Redis (for production‑ready shared sessions) in your app.


Quick Start

Minimal server with Google OIDC

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/pborman/uuid"
	oauthgo "github.com/zekith/oauthgo/api"
	oauthgocookie "github.com/zekith/oauthgo/core/cookie"
	authogodeps "github.com/zekith/oauthgo/core/deps"
	oauthgoreplay "github.com/zekith/oauthgo/core/replay"
	oauthgostore "github.com/zekith/oauthgo/core/store"
	oauthgotypes "github.com/zekith/oauthgo/core/types"
	oauthgogoogle "github.com/zekith/oauthgo/provider/google"
)

const (
	serverPort        = ":3000"
	sessionCookieName = "oauthgo_session"
	sessionTTLDays    = 30
)

func main() {
	// Initialize the dependencies using Redis for session store and replay protection
	initDependencies()

	handler := oauthgo.HandlerFacade{}

	r := gin.Default()
	providerName := "google"

	// Create and register the Google OAuth2 provider
	provider, err := oauthgogoogle.NewWithOptions(
		&oauthgotypes.ProviderConfig{
			ClientID:     os.Getenv("GOOGLE_KEY"),
			ClientSecret: os.Getenv("GOOGLE_SECRET"),
			OAuth2ODICOptions: &oauthgotypes.OAuth2OIDCOptions{
				// Mode:   pointer.To(oauthgotypes.OIDC), // Override defaults if needed
				OAuth2: &oauthgotypes.OAuth2Options{
					// Override defaults if needed
				},
			},
		})
	if err != nil {
		log.Fatal("failed to create google provider: ", err)
	}
	handler.Register(providerName, provider)

	r.GET(fmt.Sprintf("/auth/%s", providerName), gin.WrapF(
		handler.Login(providerName, oauthgo.AuthURLOptions{
			RedirectURL: "http://localhost:3000/callback/google", // Your callback URL
		})),
	)

	r.GET(fmt.Sprintf("/callback/%s", providerName), gin.WrapF(
		handler.Callback(providerName, oauthgo.CallbackOptions{
			SetLoginCookie: true, // Set to true to enable session cookie
			SetSIDCookie:   true, // Set to true to enable session ID cookie
			StoreSession:   true, // Set to true to store a session in the session store
			OnError: func(w http.ResponseWriter, r *http.Request, err error) {
				// Handle the error appropriately
				http.Error(w, "oidc auto-callback failed: "+err.Error(), http.StatusInternalServerError)
			},
			OnSuccess: func(w http.ResponseWriter, r *http.Request, res *oauthgo.CallbackResult) {
				// For demo purposes, we return the user info and session data as JSON
				// In production; you might want to redirect the user to a different page
				w.Header().Set("Content-Type", "application/json; charset=utf-8")
				_ = json.NewEncoder(w).Encode(map[string]any{
					"status":   "ok",
					"provider": res.ProviderName,
					"user":     res.User,
					"sid":      res.SID,
					"session":  res.Session,
				})
			},
		})),
	)

	log.Printf("listening on %s", serverPort)
	if err := r.Run(serverPort); err != nil {
		log.Fatal(err)
	}
}

func initDependencies() {
	deps := &authogodeps.OAuthGoDeps{
		ReplayProtector: oauthgoreplay.NewMemoryReplayProtector(), // Use a redis replay protector in production
		SessionStore:    oauthgostore.NewMemorySessionStore(),     // Use a redis session store in production
		//SessionCookieManager: oauthgocookie.GetDefaultHMACCookieSessionManager(),
		SessionCookieManager: &oauthgocookie.HMACSessionCookieManager{
			Name:       sessionCookieName,
			Secret:     []byte(uuid.New()),
			TTL:        time.Hour * 24 * sessionTTLDays,
			Secure:     false, // Set to true in production
			Domain:     "",
			HttpOnly:   true,
			CookiePath: "/",
			SameSite:   http.SameSiteLaxMode,
		},
	}
	authogodeps.Init(deps)
}



Core Concepts

Providers & Options

Each provider is described using the OAuth2OIDCOptions struct, which includes the following key fields:

  • Mode:
    Specifies the authentication mode — either:

    • OIDC (OpenID Connect) or
    • OAuth2Only (for providers that don't support OIDC)
  • OAuth2:
    Contains OAuth2-specific configuration including:

    • Authorization and token endpoint URLs
    • Optional revocation endpoint
    • Scopes
    • PKCE (Proof Key for Code Exchange) flags
  • OIDC:
    Contains OIDC-specific configuration:

    • Issuer URL
    • Optional JWKS (JSON Web Key Set) URL
    • UserInfo endpoint URL

Notes:
  • In most cases, you only need to override scopes. Endpoint URLs are preset in provider defaults but can be overridden.
  • You can override any field in OAuth2 and OIDC, except for a few non-overridable fields that are hardcoded in provider presets.
  • If Mode = OIDC, the OIDC scopes are used for authorization and token requests.
  • When overriding scopes via provider presets, ensure that all required scopes are explicitly set, as the override completely replaces the default scopes.
Handler Facade

HandlerFacade orchestrates login and callback for all registered providers and exposes plug‑and‑play net/http handlers:

  • Register(provider) -> Registers the given provider.
  • Login(provider, authURLOptions) → Builds authorization request (scopes, prompt, redirect), computes state/nonce/PKCE, and redirects to the provider.
  • Callback(provider, callbackOptions) → Verifies state/nonce/PKCE, exchanges code, validates ID token (OIDC), fetches profile when configured, and persists the session. session.
  • UserInfo(userInfoEndpoint, method) -> UserInfo returns a handler that fetches user info from the userinfo endpoint using the access token from the Authorization header.
  • Revoke(provider, token) -> Revokes the given token using the provider's revocation endpoint.
  • Refresh(provider, refreshToken) -> Refreshes the access token using the refresh token.
  • Logout() -> Logs out the current user by clearing the
  • LoggedInUser -> Returns the currently logged in user. - AutoLogin(baseUrl, provider) -> Demo helper login handler.
  • AutoCallbackOIDC(provider) -> Demo helper callback handler for OIDC providers.
  • AutoCallbackOAuth2(provider) -> Demo helper callback handler for oAuth2 providers.

These handlers can be used in your own app to configure different routes or integrate with your web framework of choice. Examples have been provided for gin and plainhttp.

A SessionStore (memory/Redis) persists auth/session data while CookieSessionManager configures cookie attributes ( Secure, HttpOnly, SameSite, MaxAge). You can swap these out for your own implementations.


Callback Options and Result

Please use the CallbackOptions struct to configure the callback handler. You can set the following options:

  • SetLoginCookie: Set to true to enable login cookie.
  • SetSIDCookie: Set to true to enable session ID cookie.
  • StoreSession: Set to true to store a session in the session store.
  • OnError: Callback for errors.
  • OnSuccess: Callback for successful callbacks.

Please refer main.go for a complete example.

Directory Layout

.
├── api/            # optional API helpers & example wiring
├── core/           # provider manager, stores, cookies, types, shared utils
├── demo/           # runnable example app (e.g., local testing, Docker)
├── provider/       # provider presets (Google, Microsoft, LinkedIn, etc.)
├── go.mod
├── go.sum
├── Makefile
└── README.md

Exact contents may evolve, but the top‑level layout follows this structure.


Sessions & Cookies

  • Memory: Great for local development; not shared across instances.
  • Redis: Recommended for production; enables horizontal scaling and sticky‑session‑free deployments.
  • Cookie: CookieSessionManager controls cookie attributes; actual session payload lives in the store, not in the cookie.

Configuration

Declare client credentials and callback URLs via environment variables or your config system. Example:

# Google
GOOGLE_KEY=xxxx.apps.googleusercontent.com
GOOGLE_SECRET=super-secret


Adding a New Provider

Provider presets are plain Go code returning *otypes.OAuth2OIDCOptions. For example, Microsoft Entra ID (Azure AD) across tenants:

package oauthgogoogle

import (
	"github.com/AlekSi/pointer"
	"github.com/zekith/oauthgo/core/provider/factory"
	coreprov "github.com/zekith/oauthgo/core/provider/oauth2oidc"
	"github.com/zekith/oauthgo/core/types"
)

var googleDefaults = &oauthgotypes.OAuth2OIDCOptions{
	Name: pointer.ToString("google"),
	Mode: pointer.To(oauthgotypes.OIDC), // Google strongly recommends OIDC

	OAuth2: &oauthgotypes.OAuth2Options{
		AuthURL:       pointer.ToString("https://accounts.google.com/o/oauth2/v2/auth"),
		TokenURL:      pointer.ToString("https://oauth2.googleapis.com/token"),
		RevocationURL: pointer.ToString("https://oauth2.googleapis.com/revoke"),
		Scopes:        pointer.To([]string{"email"}), // Applicable for OAuth2-only mode will be overridden by OIDC scopes if OIDC is enabled
		ExtraAuth: pointer.To(map[string]string{
			"access_type": "offline",
		}),
	},

	OIDC: &oauthgotypes.OIDCOptions{
		Issuer:      pointer.ToString("https://accounts.google.com"),
		Scopes:      pointer.To([]string{"openid", "profile", "email"}), // Applicable for OIDC mode
		UserInfoURL: pointer.ToString("https://openidconnect.googleapis.com/v1/userinfo"),
	},
}

func NewWithOptions(providerConfig *oauthgotypes.ProviderConfig) (coreprov.OAuthO2IDCProvider, error) {
	return oauthgofactory.NewOAuth2OIDCProvider(providerConfig, googleDefaults)
}

func GetUserInfoEndpoint() string {
	if googleDefaults.OIDC != nil && googleDefaults.OIDC.UserInfoURL != nil {
		return *googleDefaults.OIDC.UserInfoURL
	}
	return ""
}

Notes:

  • OAuth2‑only providers (e.g., GitHub) typically omit OIDCOptions and instead define a UserInfoURL against the REST API.
  • Use offline_access (or provider‑specific flag) when you need refresh tokens; not all providers issue them for web apps.

Security Checklist

  • HTTPS only in production; never transmit auth cookies over plaintext.
  • Validate state and (if OIDC) nonce; refuse callbacks with mismatches.
  • Enable PKCE for public clients; many providers support it.
  • Restrict redirect URIs to a strict allowlist; reject open redirects.
  • Set secure cookie flags: Secure, HttpOnly, SameSite=Lax/Strict, and sensible MaxAge.
  • Scope minimally; request only what you need.
  • Handle token revocation (RFC 7009) for providers that support it.
  • Log & rate‑limit callbacks to mitigate abuse.

Troubleshooting & FAQ

“Something went wrong” after consent Ensure the callback URL you registered with the provider exactly matches the one your server exposes (scheme/host/port/path). Inspect state/nonce verification logs.

“State parameter was modified” The stored state doesn’t match the callback. Clear cookies, ensure a centralized session store (e.g., Redis) across instances, and verify that nothing overwrites the state between /auth and /callback.

OIDC vs OAuth2‑only If a provider doesn’t support OIDC (no ID token), set Mode = OAuth2Only. You can still fetch user profile data via the provider’s REST API when available. Please note that OIDC supported providers will always return user response in User format as specified in oidc_provider.go for consistency but individual user endpoints output may vary depending on the provider. You need to take care of this when fetching user data.

When to use OIDC vs OAuth2 Use OIDC if you plan to "log in user" with a particular identity provider (e.g., Google, Microsoft, Apple, etc.). Use OAuth2 if you plan to access APIs on behalf of the user (e.g., GitHub, Dropbox, Box, etc.).

What if a particular provider is not supported by oauthgo? You can register your own provider using core of oauthgo without modifying oauthgo. oauthgo it just provides a convenient provider-specific default and a core facade to orchestrate login and callback. Please refermain.go for a complete example.

At the same time, please open an issue or submit a pull request. We will add support for the provider as soon as possible to help other users.

What if I need to use authentication code flow outside oAuth2/OIDC? We do not plan to support this use case. This library is designed to be used with OAuth2/OIDC providers.

What if the endpoints are changed or deprecated for a particular provider? You can always override the provider presets with your own. Please open an issue or submit a pull request. We will update the provider presets as soon as possible to help other users.

Refresh tokens Some providers (e.g., LinkedIn, GitHub in certain flows) do not return refresh tokens for typical web apps. Use short‑lived access tokens plus re‑auth, or provider‑specific grants if you need long‑lived access.

Supported Providers

Some providers are in beta and may not be fully tested. Please report any issues and submit pull requests. Please refer to this example for sample code: multiproviders Each provider can be configured via environment variables or your config system and tested using the demo app by hitting /auth/{provider} and /callback/{provider}. The demo app also serves as a reference implementation for your own app. You need to register your own OAuth2/OIDC client credentials and callback URLs.


Provider OAuth2 Support OIDC Support Refresh Tokens Token Revocation Remarks
Google Yes Yes Yes Yes
Microsoft Yes Yes Yes Yes
Okta Yes Yes Yes Yes
Auth0 Yes Yes Yes Yes
GitHub Yes No Yes No
GitLab Yes Yes Yes Yes
Bitbucket Yes No Yes No
Atlassian Yes No Yes Yes
Box Yes Yes Yes No
Dropbox Yes Yes Yes Yes
Slack Yes Yes Yes Yes
Facebook Yes Yes Yes No
Instagram (Beta) Yes No Yes No Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
LinkedIn Yes Yes Yes Yes LinkedIn generally grants 60‑day access tokens but does not issue refresh tokens for most applications; instead, you're expected to re-run the OAuth authorization flow when the access token nears expiration, and under ideal conditions (user still logged in and token not yet expired), LinkedIn can silently bypass the consent screen, resulting in a seamless refresh-like experience However, programmatic refresh tokens are available—but only to approved Marketing Developer Platform partners—and these refresh tokens last up to 365 days, with the ability to exchange them for a new access token multiple times until they themselves expire learn.microsoft.com. In summary: for typical integrations, you're unable to refresh LinkedIn access tokens server‑side and must prompt users to reauthorize periodically; only privileged partners benefit from true refresh tokens and extended automated renewal.
Salesforce Yes Yes Yes Yes
X (Twitter) Yes No Yes Yes
Discord Yes No Yes Yes
Dailymotion Yes No Yes Yes
Fitbit Yes No Yes Yes
Heroku Yes No Yes Yes
Intercom Yes No No No Access token has no automatic expiration (indefinite expiry). Manual revocation is required to invalidate it.
Kakao Yes Yes Yes Yes
Line (Beta) Yes Yes Yes Yes Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
PayPal Yes No Yes No UserInfo Endpoint is not working in Sandbox enviorment, need to verify if it works in production env
Reddit Yes No Yes Yes
Shopify (Beta) Yes No Yes No Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
Spotify Yes No Yes No
Strava Yes No Yes Yes Was not able to specify more than one scope, giving error if more than one scope is specified.
Stripe (Beta) Yes No Yes Yes Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
TikTok (Beta) Yes No Yes Yes Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
Tumblr Yes No Yes No
Uber (Beta) Yes No Yes Yes Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
Xero Yes Yes Yes Yes
Yahoo Yes Yes Yes Yes
Yandex Yes No Yes Yes
Zoom Yes No Yes Yes
Apple (Beta) Yes Partial Yes Yes Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
Twitch Yes Yes Yes Yes
Notion Yes No Yes No
Asana Yes Yes Yes Yes
ClickUp Yes No No No Access token has no automatic expiration (indefinite expiry). Manual revocation is required to invalidate it.
Monday.com Yes No No No Access token has no automatic expiration (indefinite expiry). Manual revocation is required to invalidate it.
Miro Yes No Yes Yes The availability of a refresh token depends on whether the OAuth2 application is configured to issue expiring access tokens. If token expiry is disabled, a refresh token may not be issued.
Figma Yes No Yes Yes
Square Yes No Yes Yes
DigitalOcean Yes No Yes Yes
Gitea Yes Yes Yes Yes
Hubspot Yes No Yes Yes
Zoho Yes No Yes Yes
Docusign Yes Yes Yes Yes
Boldsign Yes Yes Yes Yes
Zendesk Yes No Yes Yes
Service Now Yes No Yes Yes
Workday (Beta) Yes No Yes No Could not test due to unavailability of a test environment. Community contributions are welcome—please help test this provider and submit a pull request to update the information.
Concur (Beta) Yes No Yes No
AWS Cognito Yes Yes Yes No
Mailchimp Yes No No No Access token has no automatic expiration (indefinite expiry). Manual revocation is required to invalidate it.

Development

# Run unit tests
go test ./...

# Static checks
go vet ./...

Project structure encourages adding providers under /provider/... and keeping protocol‑specific logic under /core/....


License

MIT — see LICENSE.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL