server: support out-of-band auth flow

When "urn:ietf:wg:oauth:2.0:oob" is used as a redirect URI, redirect to
an internal dex page where the user is shown the code and instructed to
paste it into their app.
This commit is contained in:
Bobby Rullo 2016-06-20 10:37:03 -07:00
parent 42cd59aef4
commit b80dbc8975
4 changed files with 66 additions and 30 deletions

View file

@ -299,35 +299,23 @@ func getTemplates(issuerName, issuerLogoURL string,
} }
func setTemplates(srv *Server, tpls *template.Template) error { func setTemplates(srv *Server, tpls *template.Template) error {
ltpl, err := findTemplate(LoginPageTemplateName, tpls) for _, t := range []struct {
if err != nil { templateName string
return err templatePtr **template.Template
}{
{LoginPageTemplateName, &srv.LoginTemplate},
{RegisterTemplateName, &srv.RegisterTemplate},
{VerifyEmailTemplateName, &srv.VerifyEmailTemplate},
{SendResetPasswordEmailTemplateName, &srv.SendResetPasswordEmailTemplate},
{ResetPasswordTemplateName, &srv.ResetPasswordTemplate},
{OOBTemplateName, &srv.OOBTemplate},
} {
tpl, err := findTemplate(t.templateName, tpls)
if err != nil {
return err
}
*t.templatePtr = tpl
} }
srv.LoginTemplate = ltpl
rtpl, err := findTemplate(RegisterTemplateName, tpls)
if err != nil {
return err
}
srv.RegisterTemplate = rtpl
vtpl, err := findTemplate(VerifyEmailTemplateName, tpls)
if err != nil {
return err
}
srv.VerifyEmailTemplate = vtpl
srtpl, err := findTemplate(SendResetPasswordEmailTemplateName, tpls)
if err != nil {
return err
}
srv.SendResetPasswordEmailTemplate = srtpl
rpwtpl, err := findTemplate(ResetPasswordTemplateName, tpls)
if err != nil {
return err
}
srv.ResetPasswordTemplate = rpwtpl
return nil return nil
} }

View file

@ -44,6 +44,7 @@ var (
httpPathAcceptInvitation = "/accept-invitation" httpPathAcceptInvitation = "/accept-invitation"
httpPathDebugVars = "/debug/vars" httpPathDebugVars = "/debug/vars"
httpPathClientRegistration = "/registration" httpPathClientRegistration = "/registration"
httpPathOOB = "/oob"
cookieLastSeen = "LastSeen" cookieLastSeen = "LastSeen"
cookieShowEmailVerifiedMessage = "ShowEmailVerifiedMessage" cookieShowEmailVerifiedMessage = "ShowEmailVerifiedMessage"
@ -548,6 +549,37 @@ func handleTokenFunc(srv OIDCServer) http.HandlerFunc {
} }
} }
func handleOOBFunc(s *Server, tpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.Header().Set("Allow", "GET")
phttp.WriteError(w, http.StatusMethodNotAllowed, "GET only acceptable method")
return
}
key := r.URL.Query().Get("code")
if key == "" {
phttp.WriteError(w, http.StatusBadRequest, "Invalid Session")
return
}
sessionID, err := s.SessionManager.ExchangeKey(key)
if err != nil {
phttp.WriteError(w, http.StatusBadRequest, "Invalid Session")
return
}
code, err := s.SessionManager.NewSessionKey(sessionID)
if err != nil {
log.Errorf("problem getting NewSessionKey: %v", err)
phttp.WriteError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
execTemplate(w, tpl, map[string]string{
"code": code,
})
}
}
func makeHealthHandler(checks []health.Checkable) http.Handler { func makeHealthHandler(checks []health.Checkable) http.Handler {
return health.Checker{ return health.Checker{
Checks: checks, Checks: checks,

View file

@ -38,8 +38,8 @@ const (
VerifyEmailTemplateName = "verify-email.html" VerifyEmailTemplateName = "verify-email.html"
SendResetPasswordEmailTemplateName = "send-reset-password.html" SendResetPasswordEmailTemplateName = "send-reset-password.html"
ResetPasswordTemplateName = "reset-password.html" ResetPasswordTemplateName = "reset-password.html"
OOBTemplateName = "oob-template.html"
APIVersion = "v1" APIVersion = "v1"
) )
type OIDCServer interface { type OIDCServer interface {
@ -72,6 +72,7 @@ type Server struct {
VerifyEmailTemplate *template.Template VerifyEmailTemplate *template.Template
SendResetPasswordEmailTemplate *template.Template SendResetPasswordEmailTemplate *template.Template
ResetPasswordTemplate *template.Template ResetPasswordTemplate *template.Template
OOBTemplate *template.Template
HealthChecks []health.Checkable HealthChecks []health.Checkable
Connectors []connector.Connector Connectors []connector.Connector
@ -214,6 +215,7 @@ func (s *Server) HTTPHandler() http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc(httpPathDiscovery, handleDiscoveryFunc(s.ProviderConfig())) mux.HandleFunc(httpPathDiscovery, handleDiscoveryFunc(s.ProviderConfig()))
mux.HandleFunc(httpPathAuth, handleAuthFunc(s, s.Connectors, s.LoginTemplate, s.EnableRegistration)) mux.HandleFunc(httpPathAuth, handleAuthFunc(s, s.Connectors, s.LoginTemplate, s.EnableRegistration))
mux.HandleFunc(httpPathOOB, handleOOBFunc(s, s.OOBTemplate))
mux.HandleFunc(httpPathToken, handleTokenFunc(s)) mux.HandleFunc(httpPathToken, handleTokenFunc(s))
mux.HandleFunc(httpPathKeys, handleKeysFunc(s.KeyManager, clock)) mux.HandleFunc(httpPathKeys, handleKeysFunc(s.KeyManager, clock))
mux.Handle(httpPathHealth, makeHealthHandler(checks)) mux.Handle(httpPathHealth, makeHealthHandler(checks))
@ -399,6 +401,9 @@ func (s *Server) Login(ident oidc.Identity, key string) (string, error) {
} }
ru := ses.RedirectURL ru := ses.RedirectURL
if ru.String() == client.OOBRedirectURI {
ru = s.absURL(httpPathOOB)
}
q := ru.Query() q := ru.Query()
q.Set("code", code) q.Set("code", code)
q.Set("state", ses.ClientState) q.Set("state", ses.ClientState)

View file

@ -0,0 +1,11 @@
{{ template "header.html" }}
<div class="panel">
<h2 class="heading">Login Successful</h2>
Please copy this code, switch to your application and paste it there:
<br/>
<input type="text" value="{{ .code }}" />
</div>
{{ template "footer.html" }}