Merge pull request #521 from ericchiang/allow-dex-to-work-at-non-base-url

Allow dex to work at non base url
This commit is contained in:
Eric Chiang 2016-07-26 15:40:25 -07:00 committed by GitHub
commit d1bb106f94
8 changed files with 56 additions and 29 deletions

View file

@ -54,10 +54,14 @@ In order to use the `oidc` connector you must register dex as an OIDC client; th
When registering dex as a client, you need to provide redirect URLs to the provider. dex requires just one: When registering dex as a client, you need to provide redirect URLs to the provider. dex requires just one:
``` ```
https://$DEX_HOST:$DEX_PORT/auth/$CONNECTOR_ID/callback $ISSUER_URL/auth/$CONNECTOR_ID/callback
``` ```
`$DEX_HOST` and `$DEX_PORT` are the host and port of your dex installation. `$CONNECTOR_ID` is the `id` field of the connector for this OIDC provider. For example runnning a connector with ID `"google"` and an issuer URL of `"https://auth.example.com/foo"` the redirect would be.
```
https://auth.example.com/foo/auth/google/callback
```
Here's what a `oidc` connector looks like configured for authenticating with Google; the clientID and clientSecret shown are not usable. We consider Google a trusted email provider because the email address that is present in claims is for a Google provisioned email account (eg. an `@gmail.com` address) Here's what a `oidc` connector looks like configured for authenticating with Google; the clientID and clientSecret shown are not usable. We consider Google a trusted email provider because the email address that is present in claims is for a Google provisioned email account (eg. an `@gmail.com` address)
@ -82,10 +86,14 @@ This connector config lets users authenticate through [GitHub](https://github.co
To begin, register an OAuth application with GitHub through your, or your organization's [account settings](ttps://github.com/settings/applications/new). To register dex as a client of your GitHub application, enter dex's redirect URL under 'Authorization callback URL': To begin, register an OAuth application with GitHub through your, or your organization's [account settings](ttps://github.com/settings/applications/new). To register dex as a client of your GitHub application, enter dex's redirect URL under 'Authorization callback URL':
``` ```
https://$DEX_HOST:$DEX_PORT/auth/$CONNECTOR_ID/callback $ISSUER_URL/auth/$CONNECTOR_ID/callback
``` ```
`$DEX_HOST` and `$DEX_PORT` are the host and port of your dex installation. `$CONNECTOR_ID` is the `id` field of the connector. For example runnning a connector with ID `"github"` and an issuer URL of `"https://auth.example.com/bar"` the redirect would be.
```
https://auth.example.com/bar/auth/github/callback
```
Here's an example of a `github` connector; the clientID and clientSecret should be replaced by values provided by GitHub. Here's an example of a `github` connector; the clientID and clientSecret should be replaced by values provided by GitHub.
@ -113,10 +121,14 @@ __NOTE:__ When configuring a consumer through Bitbucket you _must_ configure rea
To register dex as a client of your Bitbucket consumer, enter dex's redirect URL under 'Callback URL': To register dex as a client of your Bitbucket consumer, enter dex's redirect URL under 'Callback URL':
``` ```
https://$DEX_HOST:$DEX_PORT/auth/$CONNECTOR_ID/callback $ISSUER_URL/auth/$CONNECTOR_ID/callback
``` ```
`$DEX_HOST` and `$DEX_PORT` are the host and port of your dex installation. `$CONNECTOR_ID` is the `id` field of the connector. For example runnning a connector with ID `"bitbucket"` and an issuer URL of `"https://auth.example.com/spaz"` the redirect would be.
```
https://auth.example.com/spaz/auth/bitbucket/callback
```
Here's an example of a `bitbucket` connector; the clientID and clientSecret should be replaced by values provided by Bitbucket. Here's an example of a `bitbucket` connector; the clientID and clientSecret should be replaced by values provided by Bitbucket.

View file

@ -33,7 +33,7 @@ func main() {
fs := flag.NewFlagSet("dex-worker", flag.ExitOnError) fs := flag.NewFlagSet("dex-worker", flag.ExitOnError)
listen := fs.String("listen", "http://127.0.0.1:5556", "the address that the server will listen on") listen := fs.String("listen", "http://127.0.0.1:5556", "the address that the server will listen on")
issuer := fs.String("issuer", "http://127.0.0.1:5556", "the issuer's location") issuer := fs.String("issuer", "http://127.0.0.1:5556/dex", "the issuer's location")
certFile := fs.String("tls-cert-file", "", "the server's certificate file for TLS connection") certFile := fs.String("tls-cert-file", "", "the server's certificate file for TLS connection")
keyFile := fs.String("tls-key-file", "", "the server's private key file for TLS connection") keyFile := fs.String("tls-key-file", "", "the server's private key file for TLS connection")

View file

@ -16,6 +16,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path"
"strings" "strings"
"time" "time"
@ -43,7 +44,7 @@ func main() {
certFile := fs.String("tls-cert-file", "", "the TLS cert file. If empty, the app will listen on HTTP") certFile := fs.String("tls-cert-file", "", "the TLS cert file. If empty, the app will listen on HTTP")
keyFile := fs.String("tls-key-file", "", "the TLS key file. If empty, the app will listen on HTTP") keyFile := fs.String("tls-key-file", "", "the TLS key file. If empty, the app will listen on HTTP")
discovery := fs.String("discovery", "http://127.0.0.1:5556", "") discovery := fs.String("discovery", "http://127.0.0.1:5556/dex", "")
logDebug := fs.Bool("log-debug", false, "log debug-level information") logDebug := fs.Bool("log-debug", false, "log debug-level information")
logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps") logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps")
@ -181,7 +182,7 @@ func NewClientHandler(c *oidc.Client, issuer string, cbURL url.URL) http.Handler
} }
resendURL := *issuerURL resendURL := *issuerURL
resendURL.Path = "/resend-verify-email" resendURL.Path = path.Join(resendURL.Path, "/resend-verify-email")
mux.HandleFunc("/resend", handleResendFunc(c, *issuerURL, resendURL, cbURL)) mux.HandleFunc("/resend", handleResendFunc(c, *issuerURL, resendURL, cbURL))
return mux return mux

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"path"
"strings" "strings"
"github.com/coreos/dex/connector" "github.com/coreos/dex/connector"
@ -336,7 +337,7 @@ func getConnectorForUserByEmail(ur user.UserRepo, email string) (string, error)
func newLoginURLFromSession(issuer url.URL, ses *session.Session, register bool, connectorFilter []string, msgCode string) *url.URL { func newLoginURLFromSession(issuer url.URL, ses *session.Session, register bool, connectorFilter []string, msgCode string) *url.URL {
loginURL := issuer loginURL := issuer
v := loginURL.Query() v := loginURL.Query()
loginURL.Path = httpPathAuth loginURL.Path = path.Join(loginURL.Path, httpPathAuth)
v.Set("redirect_uri", ses.RedirectURL.String()) v.Set("redirect_uri", ses.RedirectURL.String())
v.Set("state", ses.ClientState) v.Set("state", ses.ClientState)
v.Set("client_id", ses.ClientID) v.Set("client_id", ses.ClientID)

View file

@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"path" "path"
"sort" "sort"
"strings"
"time" "time"
"github.com/coreos/go-oidc/jose" "github.com/coreos/go-oidc/jose"
@ -214,41 +215,53 @@ func (s *Server) HTTPHandler() http.Handler {
clock := clockwork.NewRealClock() clock := clockwork.NewRealClock()
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc(httpPathDiscovery, handleDiscoveryFunc(s.ProviderConfig())) handle := func(urlPath string, h http.Handler) {
mux.HandleFunc(httpPathAuth, handleAuthFunc(s, s.Connectors, s.LoginTemplate, s.EnableRegistration)) p := path.Join(s.IssuerURL.Path, urlPath)
mux.HandleFunc(httpPathOOB, handleOOBFunc(s, s.OOBTemplate)) // path.Join always trims trailing slashes (https://play.golang.org/p/GRr0jDd9P7).
mux.HandleFunc(httpPathToken, handleTokenFunc(s)) // If path being registered has a trailing slash, add it back on.
mux.HandleFunc(httpPathKeys, handleKeysFunc(s.KeyManager, clock)) if strings.HasSuffix(urlPath, "/") {
mux.Handle(httpPathHealth, makeHealthHandler(checks)) p = p + "/"
}
mux.Handle(p, h)
}
handleFunc := func(urlPath string, hf http.HandlerFunc) {
handle(urlPath, hf)
}
handleFunc(httpPathDiscovery, handleDiscoveryFunc(s.ProviderConfig()))
handleFunc(httpPathAuth, handleAuthFunc(s, s.Connectors, s.LoginTemplate, s.EnableRegistration))
handleFunc(httpPathOOB, handleOOBFunc(s, s.OOBTemplate))
handleFunc(httpPathToken, handleTokenFunc(s))
handleFunc(httpPathKeys, handleKeysFunc(s.KeyManager, clock))
handle(httpPathHealth, makeHealthHandler(checks))
if s.EnableRegistration { if s.EnableRegistration {
mux.HandleFunc(httpPathRegister, handleRegisterFunc(s, s.RegisterTemplate)) handleFunc(httpPathRegister, handleRegisterFunc(s, s.RegisterTemplate))
} }
mux.HandleFunc(httpPathEmailVerify, handleEmailVerifyFunc(s.VerifyEmailTemplate, handleFunc(httpPathEmailVerify, handleEmailVerifyFunc(s.VerifyEmailTemplate,
s.IssuerURL, s.KeyManager.PublicKeys, s.UserManager)) s.IssuerURL, s.KeyManager.PublicKeys, s.UserManager))
mux.Handle(httpPathVerifyEmailResend, s.NewClientTokenAuthHandler(handleVerifyEmailResendFunc(s.IssuerURL, handle(httpPathVerifyEmailResend, s.NewClientTokenAuthHandler(handleVerifyEmailResendFunc(s.IssuerURL,
s.KeyManager.PublicKeys, s.KeyManager.PublicKeys,
s.UserEmailer, s.UserEmailer,
s.UserRepo, s.UserRepo,
s.ClientManager))) s.ClientManager)))
mux.Handle(httpPathSendResetPassword, &SendResetPasswordEmailHandler{ handle(httpPathSendResetPassword, &SendResetPasswordEmailHandler{
tpl: s.SendResetPasswordEmailTemplate, tpl: s.SendResetPasswordEmailTemplate,
emailer: s.UserEmailer, emailer: s.UserEmailer,
sm: s.SessionManager, sm: s.SessionManager,
cm: s.ClientManager, cm: s.ClientManager,
}) })
mux.Handle(httpPathResetPassword, &ResetPasswordHandler{ handle(httpPathResetPassword, &ResetPasswordHandler{
tpl: s.ResetPasswordTemplate, tpl: s.ResetPasswordTemplate,
issuerURL: s.IssuerURL, issuerURL: s.IssuerURL,
um: s.UserManager, um: s.UserManager,
keysFunc: s.KeyManager.PublicKeys, keysFunc: s.KeyManager.PublicKeys,
}) })
mux.Handle(httpPathAcceptInvitation, &InvitationHandler{ handle(httpPathAcceptInvitation, &InvitationHandler{
passwordResetURL: s.absURL(httpPathResetPassword), passwordResetURL: s.absURL(httpPathResetPassword),
issuerURL: s.IssuerURL, issuerURL: s.IssuerURL,
um: s.UserManager, um: s.UserManager,
@ -258,10 +271,10 @@ func (s *Server) HTTPHandler() http.Handler {
}) })
if s.EnableClientRegistration { if s.EnableClientRegistration {
mux.HandleFunc(httpPathClientRegistration, s.handleClientRegistration) handleFunc(httpPathClientRegistration, s.handleClientRegistration)
} }
mux.HandleFunc(httpPathDebugVars, health.ExpvarHandler) handleFunc(httpPathDebugVars, health.ExpvarHandler)
pcfg := s.ProviderConfig() pcfg := s.ProviderConfig()
for _, idpc := range s.Connectors { for _, idpc := range s.Connectors {
@ -271,7 +284,7 @@ func (s *Server) HTTPHandler() http.Handler {
} }
// NOTE(ericchiang): This path MUST end in a "/" in order to indicate a // NOTE(ericchiang): This path MUST end in a "/" in order to indicate a
// path prefix rather than an absolute path. // path prefix rather than an absolute path.
mux.Handle(path.Join(httpPathAuth, idpc.ID())+"/", idpc.Handler(*errorURL)) handle(path.Join(httpPathAuth, idpc.ID())+"/", idpc.Handler(*errorURL))
} }
apiBasePath := path.Join(httpPathAPI, APIVersion) apiBasePath := path.Join(httpPathAPI, APIVersion)
@ -280,7 +293,7 @@ func (s *Server) HTTPHandler() http.Handler {
usersAPI := usersapi.NewUsersAPI(s.UserManager, s.ClientManager, s.RefreshTokenRepo, s.UserEmailer, s.localConnectorID) usersAPI := usersapi.NewUsersAPI(s.UserManager, s.ClientManager, s.RefreshTokenRepo, s.UserEmailer, s.localConnectorID)
handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientManager).HTTPHandler() handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientManager).HTTPHandler()
mux.Handle(apiBasePath+"/", handler) handle(apiBasePath+"/", handler)
return http.Handler(mux) return http.Handler(mux)
} }

View file

@ -2,7 +2,7 @@
<div class="panel"> <div class="panel">
<h2 class="heading">Log in to Your Account</h2> <h2 class="heading">Log in to Your Account</h2>
<form method="post" action="{{.PostURL | absPath }}"> <form method="post" action="{{ .PostURL }}">
<div class="form-row"> <div class="form-row">
LDAP LDAP
<div class="input-desc"> <div class="input-desc">

View file

@ -2,7 +2,7 @@
<div class="panel"> <div class="panel">
<h2 class="heading">Log in to Your Account</h2> <h2 class="heading">Log in to Your Account</h2>
<form method="post" action="{{.PostURL | absPath}}"> <form method="post" action="{{ .PostURL }}">
<div class="form-row"> <div class="form-row">
<div class="input-desc"> <div class="input-desc">
<label for="userid">Email Address</label> <label for="userid">Email Address</label>

View file

@ -15,7 +15,7 @@
{{ end }} {{ end }}
{{ else }} {{ else }}
<h2 class="heading">Reset your password</h2> <h2 class="heading">Reset your password</h2>
<form onsubmit="return validate();" id="resetPasswordForm" method="POST" action="/reset-password"> <form onsubmit="return validate();" id="resetPasswordForm" method="POST" action="{{ "/reset-password" | absPath }}">
<div class="form-row"> <div class="form-row">
<div class="input-desc"> <div class="input-desc">
<label for="password">New Password</label> <label for="password">New Password</label>