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:
parent
42cd59aef4
commit
b80dbc8975
4 changed files with 66 additions and 30 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
11
static/html/oob-template.html
Normal file
11
static/html/oob-template.html
Normal 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" }}
|
Reference in a new issue