Documentation
¶
Overview ¶
Package frontdex provides OAuth2/OIDC authentication middleware for applications using Dex as the identity provider and supports the authorization code flow.
Index ¶
- Variables
- func AuthorizationURL(r *http.Request) string
- func FailureReason(r *http.Request) error
- func New(issuerURL, clientID, clientSecret string, opts ...Option) func(http.Handler) http.Handler
- func StateToken(r *http.Request) *http.Cookie
- func StatusCodeFromError(err error) int
- type Connector
- type Crypto
- type Option
- func WithClientRemoteIPHeader(headerName string) Option
- func WithConnectorFieldName(name string) Option
- func WithConnectors(connectors []string) Option
- func WithCookieDomain(domain string) Option
- func WithCookieHttpOnly(httpOnly bool) Option
- func WithCookieMaxAge(age int) Option
- func WithCookieName(name string) Option
- func WithCookiePath(path string) Option
- func WithCookieSameSite(sameSite SameSiteMode) Option
- func WithCookieSecure(secure bool) Option
- func WithCookieTrustedOrigins(origins []string) Option
- func WithCustomCrypto(cyp Crypto) Option
- func WithCustomTransport(rt http.RoundTripper) Option
- func WithEndpointURL(endpoint string) Option
- func WithErrorHandler(h http.Handler) Option
- func WithHTTPClientTimeout(timeout time.Duration) Option
- func WithLoginHandler(h http.Handler) Option
- func WithOAuthRedirectURL(redirectURL string) Option
- func WithOAuthScopes(scopes []string) Option
- func WithRedirectHandler(h http.Handler) Option
- func WithStateSecret(key []byte) Option
- func WithStateSecretHex(hexKey string) Option
- func WithTokenTTL(d time.Duration) Option
- func WithTrustedPeerCIDRs(cidrs []netip.Prefix) Option
- type Payload
- type SameSiteMode
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoState is returned when the OAuth2 state parameter is missing from the callback request. ErrNoState = errors.New("state missing") // ErrBadError is returned when an unrecognized error was returned in the OAuth2 callback. ErrBadError = errors.New("bad error") // ErrMissingStateToken is returned when the state token cookie is missing from the request. ErrMissingStateToken = errors.New("state token missing") // ErrBadStateToken is returned when the state token is invalid, tampered, or expired. ErrBadStateToken = errors.New("bad state token") // ErrAccessDenied is returned when the OAuth provider returns an "access_denied" or "unverified_user_email" error. // access_denied is standard, unverified_user_email is used by some providers like GitHub. ErrAccessDenied = errors.New("access denied") // ErrStateMismatch is returned when the state parameter in the callback doesn't match the initial state, // the value stored in the state token. ErrStateMismatch = errors.New("state mismatch") )
Errors.
var ( // ErrBadConnector is returned when the connector field (via) in the request is invalid or not supported. ErrBadConnector = dex.ErrInvalidConnector ErrResourceUnavailable = dex.ErrResourceUnavailable // ErrAuthFailure is returned when authentication failed at the Dex server. ErrAuthFailure = dex.ErrAuthFailure // ErrTimeout is returned when a request to Dex times out. ErrTimeout = dex.ErrTimeout // ErrNetwork is returned when a network error occurs while communicating with Dex. ErrNetwork = dex.ErrNetwork )
Errors returned from the Dex client.
var ( // ErrorHandler sends an appropriate HTTP error response based on the failure reason. // Used when an error occurs during the authentication process. Override with [WithErrorHandler]. ErrorHandler = http.HandlerFunc(errorHandler) // RedirectHandler sets the state token cookie and redirects to the authorization URL. // Used to initiate the authentication process. Override with [WithRedirectHandler]. RedirectHandler = http.HandlerFunc(redirectHandler) )
Default request handlers.
Functions ¶
func AuthorizationURL ¶
AuthorizationURL retrieves the OAuth2 authorization URL from the request context. This value is only present in redirect handler.
func FailureReason ¶
FailureReason retrieves the error value from the request context. This value is only present in error handler.
func New ¶
New returns an http.Handler that provides OAuth2/OIDC authentication using Dex as the identity provider. It intercepts requests to / and /callback to handle the authorization and callback flows, respectively. All other requests are passed to the provided handler h. You should mount this handler under a dedicated path, such as /login/.
The issuerURL, clientID, and clientSecret parameters configure the OAuth2 client and must match the values registered with your Dex server. Additional options, such as a custom Dex endpoint URL, can be provided using functional options.
Note: set issuerURL to your application (for example, https://example.com/login), not to to Dex. Dex uses the issuer URL to validate redirect URIs, but its discovery document (/.well-known/openid-configuration) will advertise /keys, /token, and other endpoints under your application's URL. If your application tried to call those endpoints, it would end up calling itself. That's why frontdex does not use OIDC discovery; it constructs the required endpoints itself and talks directly to Dex (which must be reachable from the application server). By default, frontdex assumes Dex is at http://localhost:5556; if Dex runs elsewhere, use WithEndpointURL to set the correct Dex base URL.
Upon successful authentication, the handler stores the authentication payload in the request context. You can retrieve it using the Token function. The payload is only present in GET /callback requests after successful authentication.
If authentication fails, the error is saved in the request context. Access it via FailureReason within the error handler; only requests routed to the ErrorHandler include this value. You can customize the handler with the WithErrorHandler option (recommended).
Example usage:
import "github.com/tetsuo/frontdex"
fdx := frontdex.New(
"https://example.com/login", // issuer URL
"example-client", // client ID
"change-me-in-production", // client secret
frontdex.WithLoginHandler(loginHandler), // custom login page
)
http.ListenAndServe(":8080", http.StripPrefix("/login", fdx(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if payload := frontdex.Token(r); payload != nil {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(payload)
}
}),
)))
func StateToken ¶
StateToken retrieves the state token cookie from the request context. This value is only present in redirect handler.
func StatusCodeFromError ¶
StatusCodeFromError maps known authentication and API errors to appropriate HTTP status codes for use in HTTP responses. Returns 400, 403, or 500 depending on the error type.
Types ¶
type Crypto ¶
type Crypto interface {
// Decrypt decrypts the given data.
Decrypt(data []byte) ([]byte, error)
// Encrypt encrypts the given data.
Encrypt(data []byte) ([]byte, error)
}
Crypto defines the interface for encrypting and decrypting state tokens.
type Option ¶
type Option func(*frontdex)
Option configures a frontdex instance.
func WithClientRemoteIPHeader ¶
WithClientRemoteIPHeader sets the name of the header sent to Dex containing the client's IP address. This must match the clientRemoteIP.header value in the Dex configuration. Defaults to "X-Forwarded-For".
func WithConnectorFieldName ¶
WithConnectorFieldName sets the form field name used to obtain the connector ID. Defaults to "via".
func WithConnectors ¶
WithConnectors restricts authentication to the specified Dex connector IDs. Only these connectors will be allowed when sending requests to Dex.
func WithCookieDomain ¶
WithCookieDomain sets the domain attribute for cookies provided to clients during the OAuth2 authentication flow. Defaults to the current request domain (recommended). The value is treated as being prefixed with a '.', so "example.com" also matches subdomains like "www.example.com".
func WithCookieHttpOnly ¶
WithCookieHttpOnly toggles the 'HttpOnly' flag on cookies provided to clients during the OAuth2 authentication flow. Defaults to true.
func WithCookieMaxAge ¶
WithCookieMaxAge sets the maximum age (in seconds) for cookies provided to clients as part of the OAuth2 authentication flow. Must match Dex's expiry.authRequests. Defaults to 24h.
func WithCookieName ¶
WithCookieName sets the name of the cookie provided to clients as part of the OAuth2 authentication flow. Cookie names must not contain whitespace, commas, semicolons, backslashes, or control characters, as per RFC 6265. Default value is "_fdx_chal".
func WithCookiePath ¶
WithCookiePath sets the path for cookies provided to clients during the OAuth2 authentication flow. Defaults to the path the cookie was issued from (recommended).
func WithCookieSameSite ¶
func WithCookieSameSite(sameSite SameSiteMode) Option
WithCookieSameSite sets the 'SameSite' attribute for cookies provided to clients during the OAuth2 authentication flow. Defaults to SameSiteLaxMode.
func WithCookieSecure ¶
WithCookieSecure toggles the 'Secure' flag on cookies provided to clients during the OAuth2 authentication flow. Defaults to true; disable only for local HTTP development.
func WithCookieTrustedOrigins ¶
WithCookieTrustedOrigins registers Referer origins that are treated as trusted when handling cookies during the OAuth2 authentication flow. Only include origins you own or fully control.
func WithCustomCrypto ¶
WithCustomCrypto injects a custom Crypto implementation for encrypting and decrypting state tokens. By default AES is used with a random 32-byte key. Setting a custom Crypto overrides any state secret set via WithStateSecret or WithStateSecretHex.
func WithCustomTransport ¶
func WithCustomTransport(rt http.RoundTripper) Option
WithCustomTransport sets the custom HTTP transport for requests sent to Dex.
func WithEndpointURL ¶
WithEndpointURL sets the base URL for the Dex server and configures the OAuth2 endpoints (auth, token), JWKS (keys), and userinfo URLs accordingly. Note that frontdex doesn't use the discovery protocol to obtain these URLs. Defaults to http://localhost:5556.
func WithErrorHandler ¶
WithErrorHandler overrides the default error handler for OAuth callbacks. By default, a simple text error response is returned. Note that internal server errors (500) might contain sensitive information. It's recommended to log such errors instead of displaying them to users.
For production use, consider implementing a custom error handler. For example:
WithErrorHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := frontdex.FailureReason(r)
statusCode := frontdex.StatusCodeFromError(err)
if statusCode == http.StatusInternalServerError {
// Log the error details internally
log.Printf("Internal error: %v (status: %d)", err, statusCode)
// Return a generic error message to the user
http.Error(w, "Something went wrong", statusCode)
} else {
http.Error(w, err.Error(), statusCode)
}
}))
func WithHTTPClientTimeout ¶
WithHTTPClientTimeout sets the timeout duration for requests sent to Dex. Defaults to 30s.
func WithLoginHandler ¶
WithLoginHandler overrides the default login page handler. By default, a simple HTML page with buttons for each available connector is served. The form fields use the configured connector field name (default: "via"). If you implement a custom login page (which you should), ensure that the form field names match, or adjust the connector field name using WithConnectorFieldName.
func WithOAuthRedirectURL ¶
WithOAuthRedirectURL sets the OAuth2 redirect URL used for callbacks. Must match with connector redirect URI configuration in Dex. Defaults to issuerURL + "/callback".
func WithOAuthScopes ¶
WithOAuthScopes sets the OAuth2 scopes requested from Dex during login. Default: openid, profile, email, federated:id.
func WithRedirectHandler ¶
WithRedirectHandler overrides the default redirect handler. By default, the handler sets the state token cookie and redirects to the authorization URL. For custom behavior, implement your own handler (recommended for validating auth URLs or adding additional security checks).
You can access the state token and authorization URL using StateToken and AuthorizationURL. The default handler logic is as follows:
WithRedirectHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set the state token cookie
http.SetCookie(w, frontdex.StateToken(r))
// Redirect to the authorization URL
http.Redirect(w, r, frontdex.AuthorizationURL(r), http.StatusFound)
}))
For example, add CSRF protection:
import "github.com/gorilla/csrf"
protect := csrf.Protect(...)
opts := []frontdex.Option{
frontdex.WithRedirectHandler(protect(frontdex.RedirectHandler)),
frontdex.WithLoginHandler(protect(yourLoginHandler)),
}
func WithStateSecret ¶
WithStateSecret specifies the key used by the built-in AES cipher to encrypt and decrypt state tokens. The key must be 16, 24, or 32 bytes long; if not set, a random 32-byte key is used by default.
func WithStateSecretHex ¶
WithStateSecretHex sets the state secret key using a hex-encoded string. The decoded key must be exactly 16, 24, or 32 bytes in length.
func WithTokenTTL ¶
WithTokenTTL configures the expected lifetime of the ID token issued by Dex. Used for validation. Must match Dex's expiry.idToken. Defaults to 24h.
func WithTrustedPeerCIDRs ¶
WithTrustedPeerCIDRs sets the IP ranges that are allowed to send proxy headers.
This option is REQUIRED to accept "X-Forwarded-For" headers and forward them to Dex. Only requests where RemoteAddr matches one of these CIDRs will have their headers forwarded. This should be set to the IP range of your load balancer in your trusted network.
Note that proxy header parsing is complex and out of scope for this library. frontdex only provides basic support for obtaining a single client IP from the "X-Forwarded-For" header, ignoring comma-separated IPs.
Example: For a load balancer at 10.0.0.0/24:
frontdex.New(issuerURL, clientID, clientSecret,
frontdex.WithTrustedPeerCIDRs([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/24"),
}),
)
type Payload ¶
type Payload struct {
AccessToken string `json:"access_token"`
IDToken string `json:"id_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
Claims *dex.Claims `json:"claims"`
}
Payload is the authentication payload returned after successful login.
type SameSiteMode ¶
type SameSiteMode int
SameSiteMode represents the SameSite cookie attribute modes.
const ( // SameSiteDefaultMode sets the 'SameSite' cookie attribute, which is // invalid in some older browsers due to changes in the SameSite spec. These // browsers will not send the cookie to the server. SameSiteDefaultMode SameSiteMode = iota + 1 SameSiteLaxMode // default SameSiteStrictMode SameSiteNoneMode )
SameSite options.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dex provides an HTTP client for interacting with the Dex OAuth2/OIDC provider to perform the Authorization Code flow.
|
Package dex provides an HTTP client for interacting with the Dex OAuth2/OIDC provider to perform the Authorization Code flow. |
|
internal
|
|
|
crypto
Package crypto provides an AES-GCM based implementation for frontdex.Crypto.
|
Package crypto provides an AES-GCM based implementation for frontdex.Crypto. |