auth

package
v0.30.0 Latest Latest
Warning

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

Go to latest
Published: Feb 6, 2026 License: GPL-3.0 Imports: 35 Imported by: 0

Documentation

Overview

Package auth provides authentication utilities for CERN SSO.

Package auth provides authentication utilities for CERN SSO.

Package auth provides authentication utilities for CERN SSO.

Index

Constants

View Source
const (
	Krb5ConfigEmbedded = "embedded"
	Krb5ConfigSystem   = "system"
)

Krb5ConfigSource constants for config source options.

View Source
const (
	OTPSourceFlag    = "flag"
	OTPSourceCommand = "command"
	OTPSourceEnv     = "env"
	OTPSourcePrompt  = "prompt"
)

OTP source priority constants

View Source
const (
	EnvOTP        = "CERN_SSO_OTP"
	EnvOTPCommand = "CERN_SSO_OTP_COMMAND"
)

Environment variable names for OTP configuration

View Source
const (
	MethodOTP      = "otp"
	MethodWebAuthn = "webauthn"
)

2FA method preference constants

View Source
const (
	SPATypeUnknown   = ""
	SPATypeHarbor    = "harbor"
	SPATypeOpenShift = "openshift"
)

SPAType constants for known SPA applications

View Source
const (
	WebAuthnSourceDevice  = "device"
	WebAuthnSourceBrowser = "browser"
)

WebAuthn source constants

View Source
const (
	EnvWebAuthnPIN = "CERN_SSO_WEBAUTHN_PIN"
)

Environment variable names for WebAuthn configuration

Variables

View Source
var ErrLoginFailed = errors.New("login failed")

ErrLoginFailed represents a login failure.

Functions

func AuthorizationCodeFlow

func AuthorizationCodeFlow(kerbClient *KerberosClient, cfg OIDCConfig) (string, error)

AuthorizationCodeFlow performs the OAuth2 Authorization Code flow with Kerberos.

func Check2FARequired

func Check2FARequired(body string) bool

Check2FARequired checks if the response requires 2FA.

func CheckConsentRequired

func CheckConsentRequired(body string) bool

CheckConsentRequired checks if consent is required.

func ConvertAPICacheToFile

func ConvertAPICacheToFile() (string, error)

ConvertAPICacheToFile attempts to convert macOS API credential cache to a file-based cache. Uses kinit --keychain to get tickets from keychain-stored password. Returns the path to the created file cache, or error if conversion fails.

func ConvertSpecificCacheToFile

func ConvertSpecificCacheToFile(cacheInfo *CacheInfo) (string, error)

ConvertSpecificCacheToFile converts a specific macOS API cache to a file-based cache. Uses kgetcred to export the TGT from the specified cache to a file. This allows using a non-default principal without modifying the system default.

func FindCCachePath

func FindCCachePath() string

FindCCachePath locates the Kerberos credential cache file. Returns empty string if no usable file-based cache is found.

func GetCurrentMethod added in v0.18.0

func GetCurrentMethod(body string) string

GetCurrentMethod detects which 2FA method the current page is showing. Returns "otp", "webauthn", or "" if neither is detected.

func GetErrorMessage

func GetErrorMessage(doc *goquery.Document) string

GetErrorMessage extracts the Keycloak error message from the page.

func GetErrorMessageFromHTML

func GetErrorMessageFromHTML(r io.Reader) (string, error)

GetErrorMessageFromHTML parses HTML and extracts error message.

func GetPrincipalFromKlist

func GetPrincipalFromKlist() (string, error)

GetPrincipalFromKlist parses the principal from klist output. Returns empty string if no principal found.

func GetSPALoginPage added in v0.27.0

func GetSPALoginPage(client *http.Client, spaInfo *SPAInfo, authHostname string) (string, []byte, error)

GetSPALoginPage navigates through the SPA login flow to reach the SSO page. Returns the login page URL and its body content.

func HasTryAnotherWay added in v0.18.0

func HasTryAnotherWay(body string) bool

HasTryAnotherWay checks if the page has a "Try Another Way" option.

func IsMacOSAPICCache

func IsMacOSAPICCache() bool

IsMacOSAPICCache returns true if we're on macOS and the credential cache is using the API: scheme (which is not accessible from pure Go).

func IsOTPRequired

func IsOTPRequired(body string) bool

IsOTPRequired checks if the response requires OTP authentication.

func IsRefreshable

func IsRefreshable(source string) bool

IsRefreshable returns true if the OTP source supports refresh/retry.

func IsWebAuthnAvailable

func IsWebAuthnAvailable() bool

IsWebAuthnAvailable returns true if WebAuthn support is compiled in.

func IsWebAuthnRequired

func IsWebAuthnRequired(body string) bool

IsWebAuthnRequired checks if the response requires WebAuthn/FIDO2 authentication.

func LoadKrb5Config

func LoadKrb5Config(source string) (*config.Config, error)

LoadKrb5Config loads Kerberos configuration from the specified source. source can be:

  • "" or "embedded": use the built-in CERN.CH configuration
  • "system": use system krb5.conf (KRB5_CONFIG env var or /etc/krb5.conf)
  • "/path/to/file": use a custom configuration file

func NewClientFromCCache

func NewClientFromCCache(cfg *config.Config) (*client.Client, error)

NewClientFromCCache attempts to create a Kerberos client from the credential cache. Returns nil and an error if the cache is not found, invalid, or the TGT is expired.

func NormalizePrincipal

func NormalizePrincipal(username string) string

NormalizePrincipal ensures the username has @CERN.CH suffix with correct case. Examples:

  • "clange" -> "clange@CERN.CH"
  • "clange@cern.ch" -> "clange@CERN.CH"
  • "clange@CERN.CH" -> "clange@CERN.CH" (unchanged)
  • "" -> "" (empty string returns empty)

func ParseForm

func ParseForm(r io.Reader) (action string, data url.Values, err error)

ParseForm extracts the action URL and form data from the first form in a response.

func ParseGitLabOIDCForm

func ParseGitLabOIDCForm(r io.Reader) (action string, data url.Values, err error)

ParseGitLabOIDCForm extracts the OIDC form data from a GitLab auto-submit page. GitLab puts the CSRF token in a meta tag, not in the input field.

func ParseKerberosLink(r io.Reader, authHostname string) (string, error)

ParseKerberosLink extracts the Kerberos login link from the SSO page.

func ParseSAMLForm

func ParseSAMLForm(r io.Reader) (action string, data url.Values, err error)

ParseSAMLForm extracts the SAML action URL and form data from a response.

func PostSAML

func PostSAML(client *http.Client, action string, data url.Values) (*http.Response, error)

PostSAML performs the SAML POST request.

Types

type AuthConfig added in v0.24.0

type AuthConfig struct {
	KeytabPath    string // Explicit keytab path from --keytab flag
	ForcePassword bool   // --use-password flag
	ForceKeytab   bool   // --use-keytab flag (or implied by --keytab)
	ForceCCache   bool   // --use-ccache flag
	Quiet         bool   // --quiet flag (suppress non-error output)
}

AuthConfig holds authentication method configuration.

type AuthMethod added in v0.18.0

type AuthMethod struct {
	ExecutionID string // The authenticationExecution value to submit
	Type        string // "otp" or "webauthn"
	Label       string // Human-readable label (e.g., "Authenticator Application")
}

AuthMethod represents an available 2FA authentication method.

type CacheInfo

type CacheInfo struct {
	Principal string    // e.g., "clange@CERN.CH"
	CacheName string    // e.g., "API:EE3D6722-361C-4ACD-912F-DE88264999E2"
	Expires   time.Time // Expiry time of the TGT
	IsDefault bool      // True if this is the active cache (marked with *)
}

CacheInfo contains information about a Kerberos credential cache.

func FindCacheByUsername

func FindCacheByUsername(username string) (*CacheInfo, error)

FindCacheByUsername finds a CERN.CH cache by username. The username can be provided with or without the @CERN.CH suffix. Returns error if no matching cache is found.

func ListCERNCaches

func ListCERNCaches() ([]CacheInfo, error)

ListCERNCaches returns all CERN.CH credential caches available on the system. This parses the output of `klist -l` and filters to only CERN.CH realm caches.

type FIDO2DeviceInfo added in v0.25.0

type FIDO2DeviceInfo struct {
	Index   int    // 0-based index for selection
	Path    string // Device path (e.g., /dev/hidraw0)
	Product string // Product name (e.g., "YubiKey 5 NFC")
}

FIDO2DeviceInfo contains information about an available FIDO2 device.

func ListFIDO2Devices added in v0.25.0

func ListFIDO2Devices() ([]FIDO2DeviceInfo, error)

ListFIDO2Devices returns a list of available FIDO2 devices. Returns an empty slice if no devices are found or if enumeration fails.

type HarborSystemInfo added in v0.27.0

type HarborSystemInfo struct {
	AuthMode         string `json:"auth_mode"`
	OIDCProviderName string `json:"oidc_provider_name"`
	HarborVersion    string `json:"harbor_version"`
}

HarborSystemInfo represents the Harbor systeminfo API response

type KerberosClient

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

KerberosClient handles Kerberos authentication.

func NewKerberosClient

func NewKerberosClient(version string, krb5ConfigSource string, verifyCert bool) (*KerberosClient, error)

NewKerberosClient creates a new Kerberos client with automatic authentication. This is a convenience wrapper that uses automatic authentication method selection. krb5ConfigSource can be "embedded" (default), "system", or a file path.

func NewKerberosClientWithConfig added in v0.24.0

func NewKerberosClientWithConfig(version string, krb5ConfigSource string, krbUsername string,
	verifyCert bool, authConfig AuthConfig) (*KerberosClient, error)

NewKerberosClientWithConfig creates a new Kerberos client with full configuration. This function supports explicit authentication method selection via AuthConfig, and automatic method selection when no explicit method is specified.

Authentication priority (when no explicit method is specified):

  1. Password (if KRB5_USERNAME and KRB5_PASSWORD are set)
  2. Keytab (if KRB5_KTNAME is set)
  3. Credential cache (ccache)
  4. Default keytab locations (~/.keytab, /etc/krb5.keytab)

func NewKerberosClientWithUser

func NewKerberosClientWithUser(version string, krb5ConfigSource string, krbUsername string, verifyCert bool) (*KerberosClient, error)

NewKerberosClientWithUser creates a new Kerberos client for a specific user. This is a convenience wrapper that uses automatic authentication method selection. krb5ConfigSource can be "embedded" (default), "system", or a file path.

func (*KerberosClient) Close

func (k *KerberosClient) Close()

Close cleans up the Kerberos client.

func (*KerberosClient) CollectCookies

func (k *KerberosClient) CollectCookies(targetURL string, authHostname string, result *LoginResult) ([]*http.Cookie, error)

CollectCookies collects all cookies from the session with full attributes. This uses cookies intercepted from Set-Cookie headers during the authentication flow, which preserves the original Path and Domain attributes.

func (*KerberosClient) DoSPNEGO

func (k *KerberosClient) DoSPNEGO(targetURL string) (*http.Response, error)

DoSPNEGO performs an HTTP GET request with SPNEGO authentication.

func (*KerberosClient) DoSPNEGORequest

func (k *KerberosClient) DoSPNEGORequest(req *http.Request) (*http.Response, error)

DoSPNEGORequest performs an HTTP request with SPNEGO authentication.

func (*KerberosClient) ExportCCache added in v0.26.0

func (k *KerberosClient) ExportCCache(path string) error

ExportCCache writes the internal Kerberos credentials to a ccache file.

func (*KerberosClient) GetCollectedCookies

func (k *KerberosClient) GetCollectedCookies() []*http.Cookie

GetCollectedCookies returns all cookies collected during the session with full attributes.

func (*KerberosClient) GetCookies

func (k *KerberosClient) GetCookies(u *url.URL) []*http.Cookie

GetCookies returns all cookies from the jar for a given URL. It ensures the Domain field is populated if empty (Go's cookiejar doesn't populate it).

func (*KerberosClient) GetHTTPClient

func (k *KerberosClient) GetHTTPClient() *http.Client

GetHTTPClient returns the HTTP client for non-SPNEGO requests.

func (*KerberosClient) LoginWithKerberos

func (k *KerberosClient) LoginWithKerberos(loginPage string, authHostname string, verifyCert bool) (*LoginResult, error)

LoginWithKerberos performs the full Kerberos login flow.

func (*KerberosClient) SetOTPProvider

func (k *KerberosClient) SetOTPProvider(provider *OTPProvider)

SetOTPProvider sets the OTP provider for 2FA authentication.

func (*KerberosClient) SetPreferredMethod added in v0.18.0

func (k *KerberosClient) SetPreferredMethod(method string)

SetPreferredMethod sets the preferred 2FA method. Valid values are "otp", "webauthn", or "" (use server default).

func (*KerberosClient) SetWebAuthnProvider

func (k *KerberosClient) SetWebAuthnProvider(provider *WebAuthnProvider)

SetWebAuthnProvider sets the WebAuthn provider for FIDO2 2FA authentication.

func (*KerberosClient) TryLoginWithCookies

func (k *KerberosClient) TryLoginWithCookies(targetURL string, authHostname string, cookies []*http.Cookie) (*LoginResult, error)

TryLoginWithCookies attempts to authenticate using existing auth.cern.ch cookies. This is useful for reusing existing SSO session cookies instead of performing full Kerberos authentication for each new CERN subdomain.

Example flow:

  1. User authenticates to account.web.cern.ch with Kerberos
  2. auth.cern.ch cookies are saved to cookies.txt
  3. Later, user wants to authenticate to gitlab.cern.ch
  4. TryLoginWithCookies reuses auth.cern.ch cookies
  5. Only falls back to Kerberos if cookies are expired/invalid

Returns success if cookies are valid (no redirect to auth hostname). Returns error if cookies are invalid/missing (caller should fall back to Kerberos).

type LoginError

type LoginError struct {
	Message string
}

LoginError wraps a login error with a message.

func (*LoginError) Error

func (e *LoginError) Error() string

type LoginResult

type LoginResult struct {
	Cookies     []*http.Cookie
	RedirectURI string
	Username    string // The principal that was used for authentication
}

LoginResult contains the result of a Kerberos login.

type MethodSelectionPage added in v0.18.0

type MethodSelectionPage struct {
	Action  string       // Form action URL
	Methods []AuthMethod // Available authentication methods
}

MethodSelectionPage represents the page where users can choose their 2FA method.

func ParseMethodSelectionPage added in v0.18.0

func ParseMethodSelectionPage(r io.Reader) (*MethodSelectionPage, error)

ParseMethodSelectionPage extracts the available 2FA methods from the selection page. This page is shown after clicking "Try Another Way".

func (*MethodSelectionPage) FindMethod added in v0.18.0

func (p *MethodSelectionPage) FindMethod(methodType string) *AuthMethod

FindMethod finds a method by type in the selection page. Returns nil if the method is not available.

type OIDCConfig

type OIDCConfig struct {
	AuthHostname string
	AuthRealm    string
	ClientID     string
	RedirectURI  string
	VerifyCert   bool
	Quiet        bool
}

OIDCConfig holds configuration for OIDC flows.

type OTPForm

type OTPForm struct {
	Action       string            // Form action URL
	HiddenFields map[string]string // Hidden input fields (CSRF tokens, etc.)
	OTPField     string            // Name of the OTP input field
	SubmitName   string            // Submit button name
	SubmitValue  string            // Submit button value
}

OTPForm represents the structure of an OTP login form.

func ParseOTPForm

func ParseOTPForm(r io.Reader) (*OTPForm, error)

ParseOTPForm extracts the OTP form details from the CERN 2FA page.

type OTPProvider

type OTPProvider struct {
	OTP        string // Direct OTP value (from --otp flag or CERN_SSO_OTP)
	OTPCommand string // Command to execute (from --otp-command flag or CERN_SSO_OTP_COMMAND)
	MaxRetries int    // Maximum retry attempts (default: 3)
}

OTPProvider handles OTP code retrieval from various sources. It checks sources in priority order: flag > command > env > prompt.

func (*OTPProvider) GetMaxRetries

func (p *OTPProvider) GetMaxRetries() int

GetMaxRetries returns the configured max retries, defaulting to 3.

func (*OTPProvider) GetOTP

func (p *OTPProvider) GetOTP(username string) (string, string, error)

GetOTP retrieves an OTP code using the configured sources. It tries sources in priority order:

  1. Direct OTP value (p.OTP)
  2. OTP command (p.OTPCommand)
  3. CERN_SSO_OTP environment variable
  4. CERN_SSO_OTP_COMMAND environment variable
  5. Interactive prompt (fallback)

Returns the OTP code and the source it was retrieved from.

func (*OTPProvider) RefreshOTP

func (p *OTPProvider) RefreshOTP(username string, source string, attempt, maxRetries int) (string, error)

RefreshOTP gets a fresh OTP for retry attempts. For command sources, it waits for TOTP window rollover then re-executes. For interactive sources, it re-prompts the user. For static flag sources, it returns an error (cannot refresh).

type SPAInfo added in v0.27.0

type SPAInfo struct {
	Type     string // SPATypeHarbor, SPATypeOpenShift, etc.
	LoginURL string // Direct login URL that triggers OIDC flow
	ClientID string // OIDC client ID (if extractable)
	BaseURL  string // Base URL of the target site
}

SPAInfo contains detected SPA configuration

func DetectHarbor added in v0.27.0

func DetectHarbor(client *http.Client, targetURL string) (*SPAInfo, error)

DetectHarbor checks for Harbor registry indicators by probing the systeminfo API.

func DetectOpenShift added in v0.27.0

func DetectOpenShift(bodyBytes []byte, targetURL string) (*SPAInfo, error)

DetectOpenShift checks for OpenShift/OKD indicators in the HTML response.

func DetectSPA added in v0.27.0

func DetectSPA(client *http.Client, targetURL string, bodyBytes []byte) (*SPAInfo, error)

DetectSPA probes the target URL for known SPA patterns. Called when ParseKerberosLink fails on the landing page. bodyBytes contains the HTML response from the initial request.

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"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse represents an OIDC token response.

func DeviceAuthorizationFlow

func DeviceAuthorizationFlow(cfg OIDCConfig) (*TokenResponse, error)

DeviceAuthorizationFlow performs the OAuth2 Device Authorization Grant flow.

func TokenExchange

func TokenExchange(cfg OIDCConfig, subjectToken, audience string) (*TokenResponse, error)

TokenExchange performs a token exchange.

type TryAnotherWayForm added in v0.18.0

type TryAnotherWayForm struct {
	Action string // Form action URL
}

TryAnotherWayForm represents the form to switch 2FA methods.

func ParseTryAnotherWayForm added in v0.18.0

func ParseTryAnotherWayForm(r io.Reader) (*TryAnotherWayForm, error)

ParseTryAnotherWayForm extracts the "Try Another Way" form from a 2FA page.

type WebAuthnForm

type WebAuthnForm struct {
	Action        string            // Form action URL
	Challenge     string            // Base64URL-encoded challenge
	RPID          string            // Relying Party ID
	CredentialIDs []string          // Allowed credential IDs (base64url encoded)
	UserHandle    string            // User handle (base64url encoded)
	HiddenFields  map[string]string // Other hidden input fields
}

WebAuthnForm represents the structure of a Keycloak WebAuthn form.

func ParseWebAuthnForm

func ParseWebAuthnForm(r io.Reader) (*WebAuthnForm, error)

ParseWebAuthnForm extracts the WebAuthn form details from the CERN 2FA page. Keycloak's WebAuthn page structure: - <div id="kc-form-webauthn"> is a wrapper div - <form id="webauth" action="..."> is the actual form inside - Challenge and rpId are in JavaScript, not form fields

type WebAuthnProvider

type WebAuthnProvider struct {
	DevicePath  string        // Optional: specific device path, empty = auto-detect
	DeviceIndex int           // Optional: device index (0-based), -1 = auto-detect first device
	PIN         string        // Device PIN if required
	Timeout     time.Duration // Timeout for device interaction
	UseBrowser  bool          // Fall back to browser-based flow
}

WebAuthnProvider handles FIDO2 authentication with security keys.

func (*WebAuthnProvider) Authenticate

func (p *WebAuthnProvider) Authenticate(form *WebAuthnForm) (*WebAuthnResult, error)

Authenticate performs FIDO2 assertion with the connected device. Returns the assertion data formatted for Keycloak submission.

func (*WebAuthnProvider) GetPIN

func (p *WebAuthnProvider) GetPIN() (string, error)

GetPIN retrieves the PIN using the configured sources. Priority: struct field > environment variable > interactive prompt.

func (*WebAuthnProvider) GetTimeout

func (p *WebAuthnProvider) GetTimeout() time.Duration

GetTimeout returns the configured timeout, defaulting to 30 seconds.

type WebAuthnResult

type WebAuthnResult struct {
	ClientDataJSON    string // base64url-encoded clientDataJSON
	AuthenticatorData string // base64url-encoded authenticatorData
	Signature         string // base64url-encoded signature
	CredentialID      string // base64url-encoded credential ID used
	UserHandle        string // base64url-encoded user handle (if present)
}

WebAuthnResult contains the response data to submit to Keycloak.

Directories

Path Synopsis
Package certs provides embedded CERN CA certificates.
Package certs provides embedded CERN CA certificates.

Jump to

Keyboard shortcuts

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