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 ¶
- func GenerateNonce() (string, error)
- func GenerateState() (string, error)
- type BrowserLoginResult
- type Client
- func (c *Client) AuthorizationURL(state, nonce string, pkce *PKCEChallenge) string
- func (c *Client) AuthorizationURLWithPrompt(state, nonce string, pkce *PKCEChallenge, forceLoginPrompt bool) string
- func (c *Client) ExchangeCode(ctx context.Context, code string, pkce *PKCEChallenge) (*TokenResponse, error)
- func (c *Client) LogoutURL(idTokenHint, postLogoutRedirectURI string) string
- func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error)
- type Config
- type IDTokenClaims
- type JWK
- type JWKSet
- type LocalServerConfig
- type OIDCDiscovery
- type PKCEChallenge
- type TokenResponse
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GenerateNonce ¶
GenerateNonce creates a random nonce value for token validation
func GenerateState ¶
GenerateState creates a random state value for CSRF protection
Types ¶
type BrowserLoginResult ¶
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 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) RefreshToken ¶
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
type JWKSet ¶
type JWKSet struct {
Keys []JWK `json:"keys"`
}
JWKSet represents a set of JSON Web Keys
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 ¶
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