Added introspection support in OIDC
This commit is contained in:
parent
2de1ad441a
commit
35e1452e9d
|
|
@ -296,6 +296,7 @@ Provider holds all configuration for a single provider
|
|||
| `loginURL` | _string_ | LoginURL is the authentication endpoint |
|
||||
| `redeemURL` | _string_ | RedeemURL is the token redemption endpoint |
|
||||
| `profileURL` | _string_ | ProfileURL is the profile access endpoint |
|
||||
| `introspectURL` | _string_ | IntrospectURL is the claims and permissions access Endpoint (OIDC) |
|
||||
| `resource` | _string_ | ProtectedResource is the resource that is protected (Azure AD only) |
|
||||
| `validateURL` | _string_ | ValidateURL is the access token validation endpoint |
|
||||
| `scope` | _string_ | Scope is the OAuth scope specification |
|
||||
|
|
|
|||
|
|
@ -180,9 +180,10 @@ type LegacyHeaders struct {
|
|||
PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers"`
|
||||
PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"`
|
||||
|
||||
SetBasicAuth bool `flag:"set-basic-auth" cfg:"set_basic_auth"`
|
||||
SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"`
|
||||
SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"`
|
||||
SetBasicAuth bool `flag:"set-basic-auth" cfg:"set_basic_auth"`
|
||||
SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"`
|
||||
SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"`
|
||||
SetIntrospectionResponse bool `flag:"set-introspect-response-header" cfg:"set_introspect_response_header"`
|
||||
|
||||
PreferEmailToUser bool `flag:"prefer-email-to-user" cfg:"prefer_email_to_user"`
|
||||
BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"`
|
||||
|
|
@ -200,6 +201,7 @@ func legacyHeadersFlagSet() *pflag.FlagSet {
|
|||
flagSet.Bool("set-basic-auth", false, "set HTTP Basic Auth information in response (useful in Nginx auth_request mode)")
|
||||
flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
|
||||
flagSet.Bool("set-authorization-header", false, "set Authorization response headers (useful in Nginx auth_request mode)")
|
||||
flagSet.Bool("set-introspect-response-header", false, "set the Introspect claims in the response headers")
|
||||
|
||||
flagSet.Bool("prefer-email-to-user", false, "Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. Used in conjunction with -pass-basic-auth and -pass-user-headers")
|
||||
flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
|
||||
|
|
@ -260,6 +262,9 @@ func (l *LegacyHeaders) getResponseHeaders() []Header {
|
|||
responseHeaders = append(responseHeaders, getAuthorizationHeader())
|
||||
}
|
||||
|
||||
if l.SetIntrospectionResponse {
|
||||
responseHeaders = append(responseHeaders, getXAuthIntrospectResponseHeaders())
|
||||
}
|
||||
return responseHeaders
|
||||
}
|
||||
|
||||
|
|
@ -438,6 +443,19 @@ func getXAuthRequestAccessTokenHeader() Header {
|
|||
}
|
||||
}
|
||||
|
||||
func getXAuthIntrospectResponseHeaders() Header {
|
||||
return Header{
|
||||
Name: "X-Auth-Introspect-Response",
|
||||
Values: []HeaderValue{
|
||||
{
|
||||
ClaimSource: &ClaimSource{
|
||||
Claim: "introspect-claims",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type LegacyServer struct {
|
||||
MetricsAddress string `flag:"metrics-address" cfg:"metrics_address"`
|
||||
MetricsSecureAddress string `flag:"metrics-secure-address" cfg:"metrics_secure_address"`
|
||||
|
|
@ -499,6 +517,7 @@ type LegacyProvider struct {
|
|||
LoginURL string `flag:"login-url" cfg:"login_url"`
|
||||
RedeemURL string `flag:"redeem-url" cfg:"redeem_url"`
|
||||
ProfileURL string `flag:"profile-url" cfg:"profile_url"`
|
||||
IntrospectURL string `flag:"introspect-url" cfg:"introspect_url"`
|
||||
ProtectedResource string `flag:"resource" cfg:"resource"`
|
||||
ValidateURL string `flag:"validate-url" cfg:"validate_url"`
|
||||
Scope string `flag:"scope" cfg:"scope"`
|
||||
|
|
@ -547,6 +566,7 @@ func legacyProviderFlagSet() *pflag.FlagSet {
|
|||
flagSet.String("login-url", "", "Authentication endpoint")
|
||||
flagSet.String("redeem-url", "", "Token redemption endpoint")
|
||||
flagSet.String("profile-url", "", "Profile access endpoint")
|
||||
flagSet.String("introspect-url", "", "Introspect claims access endpoint")
|
||||
flagSet.String("resource", "", "The resource that is protected (Azure AD only)")
|
||||
flagSet.String("validate-url", "", "Access token validation endpoint")
|
||||
flagSet.String("scope", "", "OAuth scope specification")
|
||||
|
|
@ -616,6 +636,7 @@ func (l *LegacyProvider) convert() (Providers, error) {
|
|||
LoginURL: l.LoginURL,
|
||||
RedeemURL: l.RedeemURL,
|
||||
ProfileURL: l.ProfileURL,
|
||||
IntrospectURL: l.IntrospectURL,
|
||||
ProtectedResource: l.ProtectedResource,
|
||||
ValidateURL: l.ValidateURL,
|
||||
Scope: l.Scope,
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ type Provider struct {
|
|||
RedeemURL string `json:"redeemURL,omitempty"`
|
||||
// ProfileURL is the profile access endpoint
|
||||
ProfileURL string `json:"profileURL,omitempty"`
|
||||
// IntrospectURL is the claims and permissions access Endpoint (OIDC)
|
||||
IntrospectURL string `json:"introspectURL,omitempty"`
|
||||
// ProtectedResource is the resource that is protected (Azure AD only)
|
||||
ProtectedResource string `json:"resource,omitempty"`
|
||||
// ValidateURL is the access token validation endpoint
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type SessionState struct {
|
|||
User string `msgpack:"u,omitempty"`
|
||||
Groups []string `msgpack:"g,omitempty"`
|
||||
PreferredUsername string `msgpack:"pu,omitempty"`
|
||||
IntrospectClaims string `msgpack:"ic,omitempty"`
|
||||
}
|
||||
|
||||
// IsExpired checks whether the session has expired
|
||||
|
|
@ -67,6 +68,9 @@ func (s *SessionState) String() string {
|
|||
if len(s.Groups) > 0 {
|
||||
o += fmt.Sprintf(" groups:%v", s.Groups)
|
||||
}
|
||||
if s.IntrospectClaims != "" {
|
||||
o += fmt.Sprintf(" Introspection Claims:%s", s.IntrospectClaims)
|
||||
}
|
||||
return o + "}"
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +99,8 @@ func (s *SessionState) GetClaim(claim string) []string {
|
|||
return groups
|
||||
case "preferred_username":
|
||||
return []string{s.PreferredUsername}
|
||||
case "introspect-claims":
|
||||
return []string{s.IntrospectClaims}
|
||||
default:
|
||||
return []string{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ func Validate(o *options.Options) error {
|
|||
o.Providers[0].ProfileURL = body.Get("userinfo_endpoint").MustString()
|
||||
}
|
||||
|
||||
if o.Providers[0].IntrospectURL == "" {
|
||||
o.Providers[0].IntrospectURL = body.Get("introspection_endpoint").MustString()
|
||||
}
|
||||
|
||||
o.Providers[0].OIDCConfig.SkipDiscovery = true
|
||||
}
|
||||
}
|
||||
|
|
@ -208,6 +212,7 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
|
|||
p.LoginURL, msgs = parseURL(o.Providers[0].LoginURL, "login", msgs)
|
||||
p.RedeemURL, msgs = parseURL(o.Providers[0].RedeemURL, "redeem", msgs)
|
||||
p.ProfileURL, msgs = parseURL(o.Providers[0].ProfileURL, "profile", msgs)
|
||||
p.IntrospectURL, msgs = parseURL(o.Providers[0].IntrospectURL, "introspect", msgs)
|
||||
p.ValidateURL, msgs = parseURL(o.Providers[0].ValidateURL, "validate", msgs)
|
||||
p.ProtectedResource, msgs = parseURL(o.Providers[0].ProtectedResource, "resource", msgs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
package providers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
|
|
@ -52,6 +55,13 @@ func (p *OIDCProvider) Redeem(ctx context.Context, redirectURL, code string) (*s
|
|||
// EnrichSession is called after Redeem to allow providers to enrich session fields
|
||||
// such as User, Email, Groups with provider specific API calls.
|
||||
func (p *OIDCProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
|
||||
if p.IntrospectURL.String() != "" {
|
||||
err := p.enrichFromIntrospectURL(ctx, s)
|
||||
if err != nil {
|
||||
logger.Errorf("Warning: Introspect URL request failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.ProfileURL.String() == "" {
|
||||
if s.Email == "" {
|
||||
return errors.New("id_token did not contain an email and profileURL is not defined")
|
||||
|
|
@ -107,6 +117,38 @@ func (p *OIDCProvider) enrichFromProfileURL(ctx context.Context, s *sessions.Ses
|
|||
return nil
|
||||
}
|
||||
|
||||
// enrichFromIntrospectURL enriches a session's claims and permissions via the JSON response of
|
||||
// an OIDC Introspection URL
|
||||
func (p *OIDCProvider) enrichFromIntrospectURL(ctx context.Context, s *sessions.SessionState) error {
|
||||
clientSecret, err := p.GetClientSecret()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Add("token", s.AccessToken)
|
||||
basicAuth := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", p.ClientID, clientSecret)))
|
||||
respJSON, err := requests.New(p.IntrospectURL.String()).
|
||||
WithContext(ctx).
|
||||
WithMethod("POST").
|
||||
WithBody(bytes.NewBufferString(params.Encode())).
|
||||
SetHeader("Authorization", fmt.Sprintf("Basic %s", basicAuth)).
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||
Do().
|
||||
UnmarshalJSON()
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("Warning: Error while fetching introspection claims , error %s", err)
|
||||
return err
|
||||
}
|
||||
b, err := respJSON.MarshalJSON()
|
||||
if err != nil {
|
||||
logger.Errorf("Cannot convert to JSON , error %s", err)
|
||||
return err
|
||||
}
|
||||
s.IntrospectClaims = b64.StdEncoding.EncodeToString([]byte(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSession checks that the session's IDToken is still valid
|
||||
func (p *OIDCProvider) ValidateSession(ctx context.Context, s *sessions.SessionState) bool {
|
||||
_, err := p.Verifier.Verify(ctx, s.IDToken)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ type ProviderData struct {
|
|||
LoginURL *url.URL
|
||||
RedeemURL *url.URL
|
||||
ProfileURL *url.URL
|
||||
IntrospectURL *url.URL
|
||||
ProtectedResource *url.URL
|
||||
ValidateURL *url.URL
|
||||
// Auth request params & related, see
|
||||
|
|
|
|||
Loading…
Reference in New Issue