mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-03 16:59:22 +02:00
core/authenticate: refactor identity authenticators to initiate redirect (#4858)
* core/authenticate: refactor identity authenticators to initiate redirect, use cookie for redirect url for cognito * set secure and http only, update test
This commit is contained in:
parent
4c15b202d1
commit
3adbc65d37
14 changed files with 237 additions and 125 deletions
|
@ -6,9 +6,11 @@ package auth0
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/identity/oauth"
|
||||
pom_oidc "github.com/pomerium/pomerium/internal/identity/oidc"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
|
@ -50,16 +52,16 @@ func (p *Provider) Name() string {
|
|||
return Name
|
||||
}
|
||||
|
||||
// GetSignOutURL implements logout as described in https://auth0.com/docs/api/authentication#logout.
|
||||
func (p *Provider) GetSignOutURL(_, redirectToURL string) (string, error) {
|
||||
// SignOut implements logout as described in https://auth0.com/docs/api/authentication#logout.
|
||||
func (p *Provider) SignOut(w http.ResponseWriter, r *http.Request, _, authenticateSignedOutURL, redirectToURL string) error {
|
||||
oa, err := p.GetOauthConfig()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting auth0 oauth config: %w", err)
|
||||
return fmt.Errorf("error getting auth0 oauth config: %w", err)
|
||||
}
|
||||
|
||||
authURL, err := urlutil.ParseAndValidateURL(oa.Endpoint.AuthURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing auth0 endpoint auth url: %w", err)
|
||||
return fmt.Errorf("error parsing auth0 endpoint auth url: %w", err)
|
||||
}
|
||||
|
||||
logoutQuery := url.Values{
|
||||
|
@ -67,10 +69,14 @@ func (p *Provider) GetSignOutURL(_, redirectToURL string) (string, error) {
|
|||
}
|
||||
if redirectToURL != "" {
|
||||
logoutQuery.Set("returnTo", redirectToURL)
|
||||
} else if authenticateSignedOutURL != "" {
|
||||
logoutQuery.Set("returnTo", authenticateSignedOutURL)
|
||||
}
|
||||
logoutURL := authURL.ResolveReference(&url.URL{
|
||||
Path: "/v2/logout",
|
||||
RawQuery: logoutQuery.Encode(),
|
||||
})
|
||||
return logoutURL.String(), nil
|
||||
|
||||
httputil.Redirect(w, r, logoutURL.String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -53,9 +53,11 @@ func TestProvider(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
|
||||
t.Run("GetSignOutURL", func(t *testing.T) {
|
||||
signOutURL, err := p.GetSignOutURL("", "https://www.example.com?a=b")
|
||||
t.Run("SignOut", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodGet, "https://authenticate.example.com/.pomerium/sign_out", nil)
|
||||
err := p.SignOut(w, r, "", "https://authenticate.example.com/.pomerium/signed_out", "https://www.example.com?a=b")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, srv.URL+"/v2/logout?client_id=CLIENT_ID&returnTo=https%3A%2F%2Fwww.example.com%3Fa%3Db", signOutURL)
|
||||
assert.Equal(t, srv.URL+"/v2/logout?client_id=CLIENT_ID&returnTo=https%3A%2F%2Fwww.example.com%3Fa%3Db", w.Header().Get("Location"))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ package cognito
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/identity/oauth"
|
||||
pom_oidc "github.com/pomerium/pomerium/internal/identity/oidc"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
|
@ -51,27 +53,31 @@ func New(ctx context.Context, opts *oauth.Options) (*Provider, error) {
|
|||
return &p, nil
|
||||
}
|
||||
|
||||
// GetSignOutURL gets the sign out URL according to https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html.
|
||||
func (p *Provider) GetSignOutURL(_, returnToURL string) (string, error) {
|
||||
// SignOut implements sign out according to https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html.
|
||||
func (p *Provider) SignOut(w http.ResponseWriter, r *http.Request, _, authenticateSignedOutURL, returnToURL string) error {
|
||||
oa, err := p.GetOauthConfig()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting cognito oauth config: %w", err)
|
||||
return fmt.Errorf("error getting cognito oauth config: %w", err)
|
||||
}
|
||||
|
||||
authURL, err := urlutil.ParseAndValidateURL(oa.Endpoint.AuthURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting cognito endpoint auth url: %w", err)
|
||||
return fmt.Errorf("error getting cognito endpoint auth url: %w", err)
|
||||
}
|
||||
|
||||
logOutQuery := url.Values{
|
||||
"client_id": []string{oa.ClientID},
|
||||
}
|
||||
if authenticateSignedOutURL != "" {
|
||||
logOutQuery.Set("logout_uri", authenticateSignedOutURL)
|
||||
}
|
||||
if returnToURL != "" {
|
||||
logOutQuery.Set("logout_uri", returnToURL)
|
||||
httputil.SetSignedOutRedirectURICookie(w, returnToURL)
|
||||
}
|
||||
logOutURL := authURL.ResolveReference(&url.URL{
|
||||
Path: "/logout",
|
||||
RawQuery: logOutQuery.Encode(),
|
||||
})
|
||||
return logOutURL.String(), nil
|
||||
httputil.Redirect(w, r, logOutURL.String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -53,9 +53,19 @@ func TestProvider(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
|
||||
t.Run("GetSignOutURL", func(t *testing.T) {
|
||||
signOutURL, err := p.GetSignOutURL("", "https://www.example.com?a=b")
|
||||
t.Run("SignOut", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodGet, "https://authenticate.example.com/.pomerium/sign_out", nil)
|
||||
err := p.SignOut(w, r, "", "https://authenticate.example.com/.pomerium/signed_out", "https://www.example.com?a=b")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, srv.URL+"/logout?client_id=CLIENT_ID&logout_uri=https%3A%2F%2Fwww.example.com%3Fa%3Db", signOutURL)
|
||||
assert.Equal(t, srv.URL+"/logout?client_id=CLIENT_ID&logout_uri=https%3A%2F%2Fauthenticate.example.com%2F.pomerium%2Fsigned_out", w.Header().Get("Location"))
|
||||
assert.Equal(t, []*http.Cookie{{
|
||||
Name: "_pomerium_signed_out_redirect_uri",
|
||||
Value: "https://www.example.com?a=b",
|
||||
MaxAge: 300,
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
Raw: "_pomerium_signed_out_redirect_uri=https://www.example.com?a=b; Max-Age=300; HttpOnly; Secure",
|
||||
}}, w.Result().Cookies())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -96,56 +96,26 @@ func New(ctx context.Context, o *oauth.Options, options ...Option) (*Provider, e
|
|||
return p, nil
|
||||
}
|
||||
|
||||
// GetSignInURL returns the url of the provider's OAuth 2.0 consent page
|
||||
// SignIn redirects to the url of the provider's OAuth 2.0 consent page
|
||||
// that asks for permissions for the required scopes explicitly.
|
||||
//
|
||||
// State is a token to protect the user from CSRF attacks. You must
|
||||
// always provide a non-empty string and validate that it matches the
|
||||
// the state query parameter on your redirect callback.
|
||||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
||||
func (p *Provider) GetSignInURL(state string) (string, error) {
|
||||
func (p *Provider) SignIn(w http.ResponseWriter, r *http.Request, state string) error {
|
||||
oa, err := p.GetOauthConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
opts := defaultAuthCodeOptions
|
||||
for k, v := range p.AuthCodeOptions {
|
||||
opts = append(opts, oauth2.SetAuthURLParam(k, v))
|
||||
}
|
||||
return oa.AuthCodeURL(state, opts...), nil
|
||||
}
|
||||
|
||||
// GetSignOutURL returns the EndSessionURL endpoint to allow a logout
|
||||
// session to be initiated.
|
||||
// https://openid.net/specs/openid-connect-frontchannel-1_0.html#RPInitiated
|
||||
func (p *Provider) GetSignOutURL(idTokenHint, redirectToURL string) (string, error) {
|
||||
_, err := p.GetProvider()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if p.EndSessionURL == "" {
|
||||
return "", ErrSignoutNotImplemented
|
||||
}
|
||||
|
||||
endSessionURL, err := urlutil.ParseAndValidateURL(p.EndSessionURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
params := endSessionURL.Query()
|
||||
if idTokenHint != "" {
|
||||
params.Add("id_token_hint", idTokenHint)
|
||||
}
|
||||
if oa, err := p.GetOauthConfig(); err == nil {
|
||||
params.Add("client_id", oa.ClientID)
|
||||
}
|
||||
if redirectToURL != "" {
|
||||
params.Add("post_logout_redirect_uri", redirectToURL)
|
||||
}
|
||||
endSessionURL.RawQuery = params.Encode()
|
||||
return endSessionURL.String(), nil
|
||||
signInURL := oa.AuthCodeURL(state, opts...)
|
||||
httputil.Redirect(w, r, signInURL, http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Authenticate converts an authorization code returned from the identity
|
||||
|
@ -340,3 +310,38 @@ func (p *Provider) GetOauthConfig() (*oauth2.Config, error) {
|
|||
}
|
||||
return p.cfg.getOauthConfig(pp), nil
|
||||
}
|
||||
|
||||
// SignOut uses the EndSessionURL endpoint to allow a logout session to be initiated.
|
||||
// https://openid.net/specs/openid-connect-frontchannel-1_0.html#RPInitiated
|
||||
func (p *Provider) SignOut(w http.ResponseWriter, r *http.Request, idTokenHint, authenticateSignedOutURL, redirectToURL string) error {
|
||||
_, err := p.GetProvider()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.EndSessionURL == "" {
|
||||
return ErrSignoutNotImplemented
|
||||
}
|
||||
|
||||
endSessionURL, err := urlutil.ParseAndValidateURL(p.EndSessionURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := endSessionURL.Query()
|
||||
if idTokenHint != "" {
|
||||
params.Add("id_token_hint", idTokenHint)
|
||||
}
|
||||
if oa, err := p.GetOauthConfig(); err == nil {
|
||||
params.Add("client_id", oa.ClientID)
|
||||
}
|
||||
if redirectToURL != "" {
|
||||
params.Add("post_logout_redirect_uri", redirectToURL)
|
||||
} else if authenticateSignedOutURL != "" {
|
||||
params.Add("post_logout_redirect_uri", authenticateSignedOutURL)
|
||||
}
|
||||
endSessionURL.RawQuery = params.Encode()
|
||||
|
||||
httputil.Redirect(w, r, endSessionURL.String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue