Skip to content

Commit

Permalink
Custom auth proxy connector with additional headers
Browse files Browse the repository at this point in the history
  • Loading branch information
aelkiss committed May 18, 2021
1 parent a1adf86 commit 4668245
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
125 changes: 125 additions & 0 deletions connector/authproxyshib/authproxy_shib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Package authproxyshib implements a connector which relies on external
// authentication (e.g. mod_auth in Apache2) and returns an identity with
// claims populated with configurable header values.
//
// The primary use is to proxy a SAML SP running Shibboleth to OIDC
package authproxyshib

import (
"fmt"
"net/http"
"net/url"
"strings"

"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)

// Config holds the configuration parameters for a connector which returns an
// identity with the HTTP header X-Remote-User as verified email.
type Config struct {
UserIDHeader string `json:"userIDHeader"`
UsernameHeader string `json:"usernameHeader"`
PreferredUsernameHeader string `json:"preferredUsernameHeader"`
EmailHeader string `json:"emailHeader"`
EmailVerifiedIfPresent bool `json:"emailVerifiedIfPresent"`
GroupsHeader string `json:"groupsHeader"`
GroupsDelimiter string `json:"groupsDelimiter"`
}

// Open returns an authentication strategy which requires no user interaction.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
userIDHeader := c.UserIDHeader
if userIDHeader == "" {
userIDHeader = "X-Remote-User"
}

groupsDelimiter := c.GroupsDelimiter
if groupsDelimiter == "" {
groupsDelimiter = ";"
}

return &callback{
userIDHeader: userIDHeader,
usernameHeader: c.UsernameHeader,
preferredUsernameHeader: c.PreferredUsernameHeader,
emailHeader: c.EmailHeader,
emailVerifiedIfPresent: c.EmailVerifiedIfPresent,
groupsHeader: c.GroupsHeader,
groupsDelimiter: groupsDelimiter,
logger: logger,
pathSuffix: "/" + id,
}, nil
}

// Callback is a connector which returns an identity with the HTTP header
// X-Remote-User as verified email.
type callback struct {
userIDHeader string
usernameHeader string
preferredUsernameHeader string
emailHeader string
emailVerifiedIfPresent bool
groupsHeader string
groupsDelimiter string
logger log.Logger
pathSuffix string
}

// LoginURL returns the URL to redirect the user to login with.
func (m *callback) LoginURL(s connector.Scopes, callbackURL, state string) (string, error) {
u, err := url.Parse(callbackURL)
if err != nil {
return "", fmt.Errorf("failed to parse callbackURL %q: %v", callbackURL, err)
}
u.Path += m.pathSuffix
v := u.Query()
v.Set("state", state)
u.RawQuery = v.Encode()
return u.String(), nil
}

// HandleCallback parses the request and returns the user's identity
func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connector.Identity, error) {
m.logger.Debugf("Headers: %v", r.Header)
userID := r.Header.Get(m.userIDHeader)
if userID == "" {
return connector.Identity{}, fmt.Errorf("required HTTP header %s is not set", m.userIDHeader)
}

identity := connector.Identity{
UserID: userID,
}

if m.usernameHeader != "" {
username := r.Header.Get(m.usernameHeader)
if username != "" {
identity.Username = username
}
}

if m.preferredUsernameHeader != "" {
preferredUsername := r.Header.Get(m.preferredUsernameHeader)
if preferredUsername != "" {
identity.PreferredUsername = preferredUsername
}
}

if m.emailHeader != "" {
email := r.Header.Get(m.emailHeader)
if email != "" {
identity.Email = email
// TODO: what happens if missing from the config?
identity.EmailVerified = m.emailVerifiedIfPresent
}
}

if m.groupsHeader != "" {
groups := r.Header.Get(m.groupsHeader)
if groups != "" {
identity.Groups = strings.Split(groups, m.groupsDelimiter)
}
}

return identity, nil
}
2 changes: 2 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/connector/atlassiancrowd"
"github.com/dexidp/dex/connector/authproxy"
"github.com/dexidp/dex/connector/authproxyshib"
"github.com/dexidp/dex/connector/bitbucketcloud"
"github.com/dexidp/dex/connector/gitea"
"github.com/dexidp/dex/connector/github"
Expand Down Expand Up @@ -503,6 +504,7 @@ var ConnectorsConfig = map[string]func() ConnectorConfig{
"oidc": func() ConnectorConfig { return new(oidc.Config) },
"saml": func() ConnectorConfig { return new(saml.Config) },
"authproxy": func() ConnectorConfig { return new(authproxy.Config) },
"authproxy-shib": func() ConnectorConfig { return new(authproxyshib.Config) },
"linkedin": func() ConnectorConfig { return new(linkedin.Config) },
"microsoft": func() ConnectorConfig { return new(microsoft.Config) },
"bitbucket-cloud": func() ConnectorConfig { return new(bitbucketcloud.Config) },
Expand Down

0 comments on commit 4668245

Please sign in to comment.