store raw id token so it can be passed to the logout url (#1543)

This commit is contained in:
Caleb Doxsey 2020-10-26 10:20:23 -06:00 committed by GitHub
parent 6c4dfcfa88
commit a85b3b04c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 112 additions and 53 deletions

View file

@ -11,6 +11,17 @@ import (
"github.com/pomerium/pomerium/pkg/protoutil"
)
// SessionClaims are claims that are attached to a session so we can store the raw id token.
type SessionClaims struct {
Claims
RawIDToken string
}
// SetRawIDToken sets the raw id token.
func (claims *SessionClaims) SetRawIDToken(rawIDToken string) {
claims.RawIDToken = rawIDToken
}
// Claims are JWT claims.
type Claims map[string]interface{}

View file

@ -0,0 +1,7 @@
// Package identity is a package to avoid a dependency cycle.
package identity
// State is the state for authentication.
type State interface {
SetRawIDToken(rawIDToken string)
}

View file

@ -15,6 +15,7 @@ import (
"gopkg.in/tomb.v2"
"github.com/pomerium/pomerium/internal/directory"
"github.com/pomerium/pomerium/internal/identity/identity"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/scheduler"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
@ -24,9 +25,9 @@ import (
// Authenticator is an identity.Provider with only the methods needed by the manager.
type Authenticator interface {
Refresh(context.Context, *oauth2.Token, interface{}) (*oauth2.Token, error)
Refresh(context.Context, *oauth2.Token, identity.State) (*oauth2.Token, error)
Revoke(context.Context, *oauth2.Token) error
UpdateUserInfo(ctx context.Context, t *oauth2.Token, v interface{}) error
UpdateUserInfo(context.Context, *oauth2.Token, interface{}) error
}
type (

View file

@ -5,6 +5,8 @@ import (
"net/url"
"golang.org/x/oauth2"
"github.com/pomerium/pomerium/internal/identity/identity"
)
// MockProvider provides a mocked implementation of the providers interface.
@ -21,12 +23,12 @@ type MockProvider struct {
}
// Authenticate is a mocked providers function.
func (mp MockProvider) Authenticate(context.Context, string, interface{}) (*oauth2.Token, error) {
func (mp MockProvider) Authenticate(context.Context, string, identity.State) (*oauth2.Token, error) {
return &mp.AuthenticateResponse, mp.AuthenticateError
}
// Refresh is a mocked providers function.
func (mp MockProvider) Refresh(context.Context, *oauth2.Token, interface{}) (*oauth2.Token, error) {
func (mp MockProvider) Refresh(context.Context, *oauth2.Token, identity.State) (*oauth2.Token, error) {
return &mp.RefreshResponse, mp.RefreshError
}

View file

@ -17,6 +17,7 @@ import (
"gopkg.in/square/go-jose.v2/jwt"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/identity/identity"
"github.com/pomerium/pomerium/internal/identity/oauth"
"github.com/pomerium/pomerium/internal/identity/oidc"
"github.com/pomerium/pomerium/internal/log"
@ -77,7 +78,7 @@ func New(ctx context.Context, o *oauth.Options) (*Provider, error) {
// Authenticate creates an identity session with github from a authorization code, and follows up
// call to the user and user group endpoint with the
func (p *Provider) Authenticate(ctx context.Context, code string, v interface{}) (*oauth2.Token, error) {
func (p *Provider) Authenticate(ctx context.Context, code string, v identity.State) (*oauth2.Token, error) {
oauth2Token, err := p.Oauth.Exchange(ctx, code)
if err != nil {
return nil, fmt.Errorf("github: token exchange failed %v", err)
@ -112,7 +113,7 @@ func (p *Provider) UpdateUserInfo(ctx context.Context, t *oauth2.Token, v interf
}
// Refresh is a no-op for github, because github sessions never expire.
func (p *Provider) Refresh(ctx context.Context, t *oauth2.Token, v interface{}) (*oauth2.Token, error) {
func (p *Provider) Refresh(ctx context.Context, t *oauth2.Token, v identity.State) (*oauth2.Token, error) {
t.Expiry = time.Now().Add(refreshDeadline)
return t, nil
}

View file

@ -15,6 +15,7 @@ import (
"golang.org/x/oauth2"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/identity/identity"
"github.com/pomerium/pomerium/internal/identity/oauth"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/internal/version"
@ -105,7 +106,7 @@ func (p *Provider) GetSignInURL(state string) string {
// Authenticate converts an authorization code returned from the identity
// provider into a token which is then converted into a user session.
func (p *Provider) Authenticate(ctx context.Context, code string, v interface{}) (*oauth2.Token, error) {
func (p *Provider) Authenticate(ctx context.Context, code string, v identity.State) (*oauth2.Token, error) {
// Exchange converts an authorization code into a token.
oauth2Token, err := p.Oauth.Exchange(ctx, code)
if err != nil {
@ -117,6 +118,10 @@ func (p *Provider) Authenticate(ctx context.Context, code string, v interface{})
return nil, fmt.Errorf("identity/oidc: failed getting id_token: %w", err)
}
if rawIDToken, ok := oauth2Token.Extra("id_token").(string); ok {
v.SetRawIDToken(rawIDToken)
}
// hydrate `v` using claims inside the returned `id_token`
// https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
if err := idToken.Claims(v); err != nil {
@ -148,7 +153,7 @@ func (p *Provider) UpdateUserInfo(ctx context.Context, t *oauth2.Token, v interf
// Refresh renews a user's session using an oidc refresh token without reprompting the user.
// Group membership is also refreshed.
// https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
func (p *Provider) Refresh(ctx context.Context, t *oauth2.Token, v interface{}) (*oauth2.Token, error) {
func (p *Provider) Refresh(ctx context.Context, t *oauth2.Token, v identity.State) (*oauth2.Token, error) {
if t == nil {
return nil, ErrMissingAccessToken
}
@ -165,6 +170,10 @@ func (p *Provider) Refresh(ctx context.Context, t *oauth2.Token, v interface{})
// https://github.com/FusionAuth/fusionauth-issues/issues/110#issuecomment-481526544
idToken, err := p.getIDToken(ctx, newToken)
if err == nil {
if rawIDToken, ok := newToken.Extra("id_token").(string); ok {
v.SetRawIDToken(rawIDToken)
}
if err := idToken.Claims(v); err != nil {
return nil, fmt.Errorf("identity/oidc: couldn't unmarshal extra claims %w", err)
}

View file

@ -10,6 +10,7 @@ import (
"golang.org/x/oauth2"
"github.com/pomerium/pomerium/internal/identity/identity"
"github.com/pomerium/pomerium/internal/identity/oauth"
"github.com/pomerium/pomerium/internal/identity/oauth/github"
"github.com/pomerium/pomerium/internal/identity/oidc"
@ -23,8 +24,8 @@ import (
// Authenticator is an interface representing the ability to authenticate with an identity provider.
type Authenticator interface {
Authenticate(context.Context, string, interface{}) (*oauth2.Token, error)
Refresh(context.Context, *oauth2.Token, interface{}) (*oauth2.Token, error)
Authenticate(context.Context, string, identity.State) (*oauth2.Token, error)
Refresh(context.Context, *oauth2.Token, identity.State) (*oauth2.Token, error)
Revoke(context.Context, *oauth2.Token) error
GetSignInURL(state string) string
Name() string