oauth

package
v1.54.2 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Example (BrowserLogin)

Example demonstrates a complete OAuth login flow with browser

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/trimble-oss/tierceron/pkg/oauth"
)

func main() {
	ctx := context.Background()

	// Create OAuth client from discovery URL
	client, err := oauth.NewClientFromDiscovery(
		"https://tierceron.test/.well-known/openid-configuration",
		"your-client-id",
		"", // Empty for public client (no client secret)
		"http://localhost:8080/callback",
		[]string{"openid", "profile", "email"},
	)
	if err != nil {
		log.Fatal(err)
	}

	// Perform browser-based login
	// This will:
	// 1. Start a local callback server
	// 2. Open the default browser to the authorization URL
	// 3. Wait for the user to complete authentication
	// 4. Receive the callback and exchange the code for tokens
	tokens, err := oauth.LoginWithBrowser(ctx, client, &oauth.LocalServerConfig{
		Port: 8080, // Use 0 for random port
	})
	if err != nil {
		log.Fatal(err)
	}

	// Parse ID token to get user information
	claims, err := oauth.ParseIDToken(tokens.IDToken)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Logged in as: %s (%s)\n", claims.Name, claims.Email)
	fmt.Printf("User ID: %s\n", claims.Subject)
}
Example (ManualFlow)

Example demonstrates manual OAuth flow without browser automation

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/trimble-oss/tierceron/pkg/oauth"
)

func main() {
	ctx := context.Background()

	// Fetch discovery document
	discovery, err := oauth.GetDiscovery("https://tierceron.test/.well-known/openid-configuration")
	if err != nil {
		log.Fatal(err)
	}

	// Create client
	client := oauth.NewClient(&oauth.Config{
		ClientID:    "your-client-id",
		RedirectURL: "http://localhost:8080/callback",
		Scopes:      []string{"openid", "profile", "email"},
		Discovery:   discovery,
	})

	// Generate PKCE challenge
	pkce, err := oauth.GeneratePKCEChallenge()
	if err != nil {
		log.Fatal(err)
	}

	// Generate state and nonce for security
	state, _ := oauth.GenerateState()
	nonce, _ := oauth.GenerateNonce()

	// Build authorization URL
	authURL := client.AuthorizationURL(state, nonce, pkce)
	fmt.Printf("Visit this URL to authenticate:\n%s\n", authURL)

	// In a real implementation, you would:
	// 1. Direct user to authURL
	// 2. Handle callback to get 'code' parameter
	// 3. Verify state matches
	// 4. Exchange code for tokens

	// Example code exchange (assuming you got the code from callback)
	code := "received-authorization-code"
	tokens, err := client.ExchangeCode(ctx, code, pkce)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Access Token: %s\n", tokens.AccessToken)
}
Example (Pkce)

Example demonstrates PKCE challenge generation

package main

import (
	"fmt"
	"log"

	"github.com/trimble-oss/tierceron/pkg/oauth"
)

func main() {
	// Generate PKCE challenge
	pkce, err := oauth.GeneratePKCEChallenge()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Code Verifier: %s\n", pkce.Verifier)
	fmt.Printf("Code Challenge: %s\n", pkce.Challenge)
	fmt.Printf("Challenge Method: %s\n", pkce.Method)
}
Example (RefreshToken)

Example demonstrates token refresh

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/trimble-oss/tierceron/pkg/oauth"
)

func main() {
	ctx := context.Background()

	client, _ := oauth.NewClientFromDiscovery(
		"https://tierceron.test/.well-known/openid-configuration",
		"your-client-id",
		"",
		"http://localhost:8080/callback",
		[]string{"openid", "profile", "email"},
	)

	// Assume you have a refresh token from a previous login
	refreshToken := "your-refresh-token"

	// Exchange refresh token for new tokens
	tokens, err := client.RefreshToken(ctx, refreshToken)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("New access token: %s\n", tokens.AccessToken)
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateNonce

func GenerateNonce() (string, error)

GenerateNonce creates a random nonce value for token validation

func GenerateState

func GenerateState() (string, error)

GenerateState creates a random state value for CSRF protection

Types

type BrowserLoginResult

type BrowserLoginResult struct {
	Code  string
	State string
	Error string
}

BrowserLoginResult holds the result of a browser-based OAuth login

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents an OAuth/OIDC client

func NewClient

func NewClient(config *Config) *Client

NewClient creates a new OAuth client with the given configuration

func NewClientFromDiscovery

func NewClientFromDiscovery(discoveryURL, clientID, clientSecret, redirectURL string, scopes []string) (*Client, error)

NewClientFromDiscovery creates a new OAuth client by fetching the discovery document

func (*Client) AuthorizationURL

func (c *Client) AuthorizationURL(state, nonce string, pkce *PKCEChallenge) string

AuthorizationURL builds the authorization URL for the OAuth flow

func (*Client) AuthorizationURLWithPrompt

func (c *Client) AuthorizationURLWithPrompt(state, nonce string, pkce *PKCEChallenge, forceLoginPrompt bool) string

AuthorizationURLWithPrompt builds the authorization URL with optional login prompt Set forceLoginPrompt to true to force a new login even if a session exists

func (*Client) ExchangeCode

func (c *Client) ExchangeCode(ctx context.Context, code string, pkce *PKCEChallenge) (*TokenResponse, error)

ExchangeCode exchanges an authorization code for tokens

func (*Client) LogoutURL

func (c *Client) LogoutURL(idTokenHint, postLogoutRedirectURI string) string

LogoutURL builds the logout URL for ending the session

func (*Client) RefreshToken

func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error)

RefreshToken exchanges a refresh token for new tokens

type Config

type Config struct {
	ClientID     string
	ClientSecret string // Optional for public clients
	RedirectURL  string
	Scopes       []string
	Discovery    *OIDCDiscovery
}

Config holds the OAuth/OIDC client configuration

type IDTokenClaims

type IDTokenClaims struct {
	Issuer        string         `json:"iss"`
	Subject       string         `json:"sub"`
	Audience      any            `json:"aud"` // Can be string or []string
	ExpiresAt     int64          `json:"exp"`
	IssuedAt      int64          `json:"iat"`
	AuthTime      int64          `json:"auth_time,omitempty"`
	Nonce         string         `json:"nonce,omitempty"`
	Email         string         `json:"email,omitempty"`
	EmailVerified bool           `json:"email_verified,omitempty"`
	Name          string         `json:"name,omitempty"`
	GivenName     string         `json:"given_name,omitempty"`
	FamilyName    string         `json:"family_name,omitempty"`
	OtherClaims   map[string]any `json:"-"`
}

IDTokenClaims represents the claims in an ID token

func ParseIDToken

func ParseIDToken(idToken string) (*IDTokenClaims, error)

ParseIDToken parses and decodes an ID token (without verification) Use VerifyIDToken for full verification

func VerifyIDToken

func VerifyIDToken(ctx context.Context, idToken string, jwksURI string, clientID string, issuer string, nonce string) (*IDTokenClaims, error)

VerifyIDToken verifies and validates an ID token This is a basic implementation - for production use, consider using a full JWT library like github.com/golang-jwt/jwt

type JWK

type JWK struct {
	Kid string `json:"kid"`
	Kty string `json:"kty"`
	Alg string `json:"alg"`
	Use string `json:"use"`
	N   string `json:"n"`
	E   string `json:"e"`
}

JWK represents a JSON Web Key

func (*JWK) GetPublicKey

func (j *JWK) GetPublicKey() (*rsa.PublicKey, error)

GetPublicKey converts a JWK to an RSA public key

type JWKSet

type JWKSet struct {
	Keys []JWK `json:"keys"`
}

JWKSet represents a set of JSON Web Keys

func FetchJWKS

func FetchJWKS(jwksURI string) (*JWKSet, error)

FetchJWKS fetches the JSON Web Key Set from the JWKS URI

type LocalServerConfig

type LocalServerConfig struct {
	Port             int           // Port to listen on (0 for random)
	Path             string        // Callback path (default: "/callback")
	ReadTimeout      time.Duration // HTTP read timeout
	WriteTimeout     time.Duration // HTTP write timeout
	ForceLoginPrompt bool          // Force login prompt even if session exists
	// HandlerRegisterFunc allows external registration of the callback handler
	// If provided, LoginWithBrowser will not create its own HTTP server
	// Instead, it calls this function to register the handler and waits for callback
	HandlerRegisterFunc func(path string, handler http.Handler) error
}

LocalServerConfig configures the local callback server

type OIDCDiscovery

type OIDCDiscovery struct {
	Issuer                            string   `json:"issuer"`
	AuthorizationEndpoint             string   `json:"authorization_endpoint"`
	TokenEndpoint                     string   `json:"token_endpoint"`
	UserinfoEndpoint                  string   `json:"userinfo_endpoint"`
	JwksURI                           string   `json:"jwks_uri"`
	EndSessionEndpoint                string   `json:"end_session_endpoint"`
	ResponseTypesSupported            []string `json:"response_types_supported"`
	SubjectTypesSupported             []string `json:"subject_types_supported"`
	IDTokenSigningAlgValuesSupported  []string `json:"id_token_signing_alg_values_supported"`
	ScopesSupported                   []string `json:"scopes_supported"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
	ClaimsSupported                   []string `json:"claims_supported"`
	CodeChallengeMethodsSupported     []string `json:"code_challenge_methods_supported"`
}

OIDCDiscovery represents the OpenID Connect discovery document

func GetDiscovery

func GetDiscovery(discoveryURL string) (*OIDCDiscovery, error)

GetDiscovery fetches the OIDC discovery document from the provider

type PKCEChallenge

type PKCEChallenge struct {
	Verifier  string
	Challenge string
	Method    string
}

PKCEChallenge holds the PKCE code verifier and challenge

func GeneratePKCEChallenge

func GeneratePKCEChallenge() (*PKCEChallenge, error)

GeneratePKCEChallenge creates a new PKCE code verifier and challenge using S256 method

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken      string `json:"id_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse represents the OAuth token response

func LoginWithBrowser

func LoginWithBrowser(ctx context.Context, client *Client, config *LocalServerConfig) (*TokenResponse, error)

LoginWithBrowser initiates an OAuth flow by opening a browser and starting a local callback server

Jump to

Keyboard shortcuts

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