mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-11 08:07:38 +02:00
authenticate: make callback path configurable (#493)
Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
This commit is contained in:
parent
1901cb5ca0
commit
5716113c2a
6 changed files with 57 additions and 26 deletions
|
@ -27,8 +27,6 @@ import (
|
||||||
"github.com/pomerium/pomerium/internal/urlutil"
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const callbackPath = "/oauth2/callback"
|
|
||||||
|
|
||||||
// ValidateOptions checks that configuration are complete and valid.
|
// ValidateOptions checks that configuration are complete and valid.
|
||||||
// Returns on first error found.
|
// Returns on first error found.
|
||||||
func ValidateOptions(o config.Options) error {
|
func ValidateOptions(o config.Options) error {
|
||||||
|
@ -47,6 +45,9 @@ func ValidateOptions(o config.Options) error {
|
||||||
if o.ClientSecret == "" {
|
if o.ClientSecret == "" {
|
||||||
return errors.New("authenticate: 'IDP_CLIENT_SECRET' is required")
|
return errors.New("authenticate: 'IDP_CLIENT_SECRET' is required")
|
||||||
}
|
}
|
||||||
|
if o.AuthenticateCallbackPath == "" {
|
||||||
|
return errors.New("authenticate: 'AUTHENTICATE_CALLBACK_PATH' is required")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +150,7 @@ func New(opts config.Options) (*Authenticate, error) {
|
||||||
headerStore := header.NewStore(encryptedEncoder, "Pomerium")
|
headerStore := header.NewStore(encryptedEncoder, "Pomerium")
|
||||||
|
|
||||||
redirectURL, _ := urlutil.DeepCopy(opts.AuthenticateURL)
|
redirectURL, _ := urlutil.DeepCopy(opts.AuthenticateURL)
|
||||||
redirectURL.Path = callbackPath
|
redirectURL.Path = opts.AuthenticateCallbackPath
|
||||||
// configure our identity provider
|
// configure our identity provider
|
||||||
provider, err := identity.New(
|
provider, err := identity.New(
|
||||||
opts.Provider,
|
opts.Provider,
|
||||||
|
|
|
@ -43,6 +43,8 @@ func TestOptions_Validate(t *testing.T) {
|
||||||
badSharedKey.SharedKey = ""
|
badSharedKey.SharedKey = ""
|
||||||
badAuthenticateURL := newTestOptions(t)
|
badAuthenticateURL := newTestOptions(t)
|
||||||
badAuthenticateURL.AuthenticateURL = nil
|
badAuthenticateURL.AuthenticateURL = nil
|
||||||
|
badCallbackPath := newTestOptions(t)
|
||||||
|
badCallbackPath.AuthenticateCallbackPath = ""
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -60,6 +62,7 @@ func TestOptions_Validate(t *testing.T) {
|
||||||
{"no client id", emptyClientID, true},
|
{"no client id", emptyClientID, true},
|
||||||
{"no client secret", emptyClientSecret, true},
|
{"no client secret", emptyClientSecret, true},
|
||||||
{"empty authenticate url", badAuthenticateURL, true},
|
{"empty authenticate url", badAuthenticateURL, true},
|
||||||
|
{"empty callback path", badCallbackPath, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (a *Authenticate) Handler() http.Handler {
|
||||||
a.cookieSecret,
|
a.cookieSecret,
|
||||||
csrf.Secure(a.cookieOptions.Secure),
|
csrf.Secure(a.cookieOptions.Secure),
|
||||||
csrf.Path("/"),
|
csrf.Path("/"),
|
||||||
csrf.UnsafePaths([]string{callbackPath}), // enforce CSRF on "safe" handler
|
csrf.UnsafePaths([]string{a.RedirectURL.Path}), // enforce CSRF on "safe" handler
|
||||||
csrf.FormValueName("state"), // rfc6749 section-10.12
|
csrf.FormValueName("state"), // rfc6749 section-10.12
|
||||||
csrf.CookieName(fmt.Sprintf("%s_csrf", a.cookieOptions.Name)),
|
csrf.CookieName(fmt.Sprintf("%s_csrf", a.cookieOptions.Name)),
|
||||||
csrf.ErrorHandler(httputil.HandlerFunc(httputil.CSRFFailureHandler)),
|
csrf.ErrorHandler(httputil.HandlerFunc(httputil.CSRFFailureHandler)),
|
||||||
|
|
|
@ -89,6 +89,12 @@ type Options struct {
|
||||||
AuthenticateURLString string `mapstructure:"authenticate_service_url" yaml:"authenticate_service_url,omitempty"`
|
AuthenticateURLString string `mapstructure:"authenticate_service_url" yaml:"authenticate_service_url,omitempty"`
|
||||||
AuthenticateURL *url.URL `yaml:"-,omitempty"`
|
AuthenticateURL *url.URL `yaml:"-,omitempty"`
|
||||||
|
|
||||||
|
// AuthenticateCallbackPath is the path to the HTTP endpoint that will
|
||||||
|
// receive the response from your identity provider. The value must exactly
|
||||||
|
// match one of the authorized redirect URIs for the OAuth 2.0 client.
|
||||||
|
// Defaults to: `/oauth2/callback`
|
||||||
|
AuthenticateCallbackPath string `mapstructure:"authenticate_callback_path" yaml:"authenticate_callback_path,omitempty"`
|
||||||
|
|
||||||
// Session/Cookie management
|
// Session/Cookie management
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||||
CookieName string `mapstructure:"cookie_name" yaml:"cookie_name,omitempty"`
|
CookieName string `mapstructure:"cookie_name" yaml:"cookie_name,omitempty"`
|
||||||
|
@ -221,6 +227,7 @@ var defaultOptions = Options{
|
||||||
GRPCClientTimeout: 10 * time.Second, // Try to withstand transient service failures for a single request
|
GRPCClientTimeout: 10 * time.Second, // Try to withstand transient service failures for a single request
|
||||||
GRPCClientDNSRoundRobin: true,
|
GRPCClientDNSRoundRobin: true,
|
||||||
CacheStore: "autocache",
|
CacheStore: "autocache",
|
||||||
|
AuthenticateCallbackPath: "/oauth2/callback",
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultOptions returns a copy the default options. It's the caller's
|
// NewDefaultOptions returns a copy the default options. It's the caller's
|
||||||
|
|
|
@ -224,6 +224,7 @@ func TestOptionsFromViper(t *testing.T) {
|
||||||
CookieSecure: true,
|
CookieSecure: true,
|
||||||
InsecureServer: true,
|
InsecureServer: true,
|
||||||
CookieHTTPOnly: true,
|
CookieHTTPOnly: true,
|
||||||
|
AuthenticateCallbackPath: "/oauth2/callback",
|
||||||
Headers: map[string]string{
|
Headers: map[string]string{
|
||||||
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
|
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
|
||||||
"X-Frame-Options": "SAMEORIGIN",
|
"X-Frame-Options": "SAMEORIGIN",
|
||||||
|
@ -235,6 +236,7 @@ func TestOptionsFromViper(t *testing.T) {
|
||||||
&Options{
|
&Options{
|
||||||
Policies: []Policy{{From: "https://from.example", To: "https://to.example"}},
|
Policies: []Policy{{From: "https://from.example", To: "https://to.example"}},
|
||||||
CookieName: "_pomerium",
|
CookieName: "_pomerium",
|
||||||
|
AuthenticateCallbackPath: "/oauth2/callback",
|
||||||
CookieSecure: true,
|
CookieSecure: true,
|
||||||
CookieHTTPOnly: true,
|
CookieHTTPOnly: true,
|
||||||
InsecureServer: true,
|
InsecureServer: true,
|
||||||
|
|
|
@ -503,6 +503,24 @@ Identity provider scopes correspond to access privilege scopes as defined in Sec
|
||||||
|
|
||||||
Identity Provider Service Account is field used to configure any additional user account or access-token that may be required for querying additional user information during authentication. For a concrete example, Google an additional service account and to make a follow-up request to query a user's group membership. For more information, refer to the [identity provider] docs to see if your provider requires this setting.
|
Identity Provider Service Account is field used to configure any additional user account or access-token that may be required for querying additional user information during authentication. For a concrete example, Google an additional service account and to make a follow-up request to query a user's group membership. For more information, refer to the [identity provider] docs to see if your provider requires this setting.
|
||||||
|
|
||||||
|
### Authenticate Callback Path
|
||||||
|
|
||||||
|
- Environmental Variable: `AUTHENTICATE_CALLBACK_PATH`
|
||||||
|
- Config File Key: `authenticate_callback_path`
|
||||||
|
- Type: `string`
|
||||||
|
- Default: `/oauth2/callback`
|
||||||
|
- Optional
|
||||||
|
|
||||||
|
The authenticate callback path is the path/url from the authenticate service that will receive the response from your identity provider. The value must exactly match one of the authorized redirect URIs for the OAuth 2.0 client.
|
||||||
|
|
||||||
|
This value is referred to as the `redirect_url` in the [OpenIDConnect](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) and OAuth2 specs.
|
||||||
|
|
||||||
|
See also:
|
||||||
|
|
||||||
|
- [OAuth2 RFC 6749](https://tools.ietf.org/html/rfc6749#section-3.1.2)
|
||||||
|
- [OIDC Spec](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)
|
||||||
|
- [Google - Setting Redirect URI](https://developers.google.com/identity/protocols/OpenIDConnect#setredirecturi)
|
||||||
|
|
||||||
## Proxy Service
|
## Proxy Service
|
||||||
|
|
||||||
### Signing Key
|
### Signing Key
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue