mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 10:13:21 +00:00
implement OIDC web server side flow
This commit is contained in:
parent
84cade14c5
commit
949df66eb3
|
@ -466,11 +466,7 @@ func (s *AuthServer) CreateOIDCAuthRequest(req services.OIDCAuthRequest) (*servi
|
|||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
sessionKey, err := utils.CryptoRandomHex(64)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
redirectURL := oauthClient.AuthCodeURL(sessionKey, "", "")
|
||||
redirectURL := oauthClient.AuthCodeURL(req.StateToken, "", "")
|
||||
req.RedirectURL = redirectURL
|
||||
|
||||
err = s.IdentityService.CreateOIDCAuthRequest(req, defaults.OIDCAuthRequestTTL)
|
||||
|
@ -556,6 +552,7 @@ func (a *AuthServer) ValidateOIDCAuthCallback(q url.Values) (*OIDCAuthResponse,
|
|||
|
||||
response := &OIDCAuthResponse{
|
||||
User: *user,
|
||||
Req: *req,
|
||||
}
|
||||
|
||||
if req.CreateWebSession {
|
||||
|
|
|
@ -653,6 +653,10 @@ type OIDCAuthRequest struct {
|
|||
// CreateWebSession indicates if user wants to generate a web
|
||||
// session after successful authentication
|
||||
CreateWebSession bool `json:"create_web_session"`
|
||||
|
||||
// ClientRedirectURL is a URL client wants to be redirected
|
||||
// after successfull authentication
|
||||
ClientRedirectURL string `json:"client_redirect_url"`
|
||||
}
|
||||
|
||||
// Check returns nil if all parameters are great, err otherwise
|
||||
|
|
|
@ -21,6 +21,8 @@ package web
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
@ -159,13 +161,34 @@ func NewHandler(cfg Config, opts ...HandlerOption) (http.Handler, error) {
|
|||
h.GET("/webapi/oidc/login", httplib.MakeHandler(h.oidcLogin))
|
||||
h.GET("/webapi/oidc/callback", httplib.MakeHandler(h.oidcCallback))
|
||||
|
||||
indexPath := filepath.Join(cfg.AssetsDir, "/index.html")
|
||||
log.Infof("INDEX PATH: %v", indexPath)
|
||||
indexContent, err := ioutil.ReadFile(indexPath)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(teleport.ConvertSystemError(err))
|
||||
}
|
||||
indexPage, err := template.New("index").Parse(string(indexContent))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(teleport.BadParameter("index", fmt.Sprintf("failed parsing template %v: %v", indexPath, err)))
|
||||
}
|
||||
|
||||
routingHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/web", http.StatusFound)
|
||||
} else if strings.HasPrefix(r.URL.Path, "/web/app") {
|
||||
http.StripPrefix("/web", http.FileServer(http.Dir(cfg.AssetsDir))).ServeHTTP(w, r)
|
||||
} else if strings.HasPrefix(r.URL.Path, "/web") {
|
||||
http.ServeFile(w, r, filepath.Join(cfg.AssetsDir, "/index.html"))
|
||||
ctx, err := h.authenticateRequest(w, r, false)
|
||||
session := struct {
|
||||
Session string
|
||||
}{Session: "null"}
|
||||
if err == nil {
|
||||
out, err := json.Marshal(newSessionResponse(ctx.GetWebSession()))
|
||||
if err == nil {
|
||||
session.Session = string(out)
|
||||
}
|
||||
}
|
||||
indexPage.Execute(w, session)
|
||||
} else if strings.HasPrefix(r.URL.Path, "/"+Version) {
|
||||
http.StripPrefix("/"+Version, h).ServeHTTP(w, r)
|
||||
}
|
||||
|
@ -176,24 +199,40 @@ func NewHandler(cfg Config, opts ...HandlerOption) (http.Handler, error) {
|
|||
|
||||
func (m *Handler) oidcLogin(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
log.Infof("oidcLogin start")
|
||||
query := r.URL.Query()
|
||||
clientRedirectURL := query.Get("redirect_url")
|
||||
if clientRedirectURL == "" {
|
||||
return nil, trace.Wrap(teleport.BadParameter("redirect_url", "missing redirect_url query parameter"))
|
||||
}
|
||||
response, err := m.cfg.ProxyClient.CreateOIDCAuthRequest(
|
||||
services.OIDCAuthRequest{
|
||||
ConnectorID: "google",
|
||||
CreateWebSession: true,
|
||||
ConnectorID: "google",
|
||||
CreateWebSession: true,
|
||||
ClientRedirectURL: clientRedirectURL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
log.Infof("got auth response: %#v", response)
|
||||
http.Redirect(w, r, response.RedirectURL, http.StatusFound)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *Handler) oidcCallback(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
log.Infof("oidcLogin validate")
|
||||
log.Infof("oidcLogin validate: %#v", r.URL.Query())
|
||||
response, err := m.cfg.ProxyClient.ValidateOIDCAuthCallback(r.URL.Query())
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
log.Infof("oidcCallback got response: %v", response)
|
||||
// if we created web session, set session cookie and redirect to original url
|
||||
if response.Req.CreateWebSession {
|
||||
if err := SetSession(w, response.User.Name, response.Session.ID); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
http.Redirect(w, r, response.Req.ClientRedirectURL, http.StatusFound)
|
||||
return nil, nil
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -905,7 +944,7 @@ type siteHandler func(w http.ResponseWriter, r *http.Request, p httprouter.Param
|
|||
// withSiteAuth ensures that request is authenticated and is issued for existing site
|
||||
func (h *Handler) withSiteAuth(fn siteHandler) httprouter.Handle {
|
||||
return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
ctx, err := h.authenticateRequest(r)
|
||||
ctx, err := h.authenticateRequest(w, r, true)
|
||||
if err != nil {
|
||||
// clear session just in case if the authentication request is not valid
|
||||
ClearSession(w)
|
||||
|
@ -930,7 +969,7 @@ func (h *Handler) withSiteAuth(fn siteHandler) httprouter.Handle {
|
|||
// withAuth ensures that request is authenticated
|
||||
func (h *Handler) withAuth(fn contextHandler) httprouter.Handle {
|
||||
return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
ctx, err := h.authenticateRequest(r)
|
||||
ctx, err := h.authenticateRequest(w, r, true)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -940,7 +979,7 @@ func (h *Handler) withAuth(fn contextHandler) httprouter.Handle {
|
|||
|
||||
// authenticateRequest authenticates request using combination of a session cookie
|
||||
// and bearer token
|
||||
func (h *Handler) authenticateRequest(r *http.Request) (*sessionContext, error) {
|
||||
func (h *Handler) authenticateRequest(w http.ResponseWriter, r *http.Request, checkBearerToken bool) (*sessionContext, error) {
|
||||
logger := log.WithFields(log.Fields{
|
||||
"request": fmt.Sprintf("%v %v", r.Method, r.URL.Path),
|
||||
})
|
||||
|
@ -954,19 +993,22 @@ func (h *Handler) authenticateRequest(r *http.Request) (*sessionContext, error)
|
|||
logger.Warningf("failed to decode cookie: %v", err)
|
||||
return nil, trace.Wrap(teleport.AccessDenied("failed to decode cookie"))
|
||||
}
|
||||
creds, err := roundtrip.ParseAuthHeaders(r)
|
||||
if err != nil {
|
||||
logger.Warningf("no auth headers %v", err)
|
||||
return nil, trace.Wrap(teleport.AccessDenied("need auth"))
|
||||
}
|
||||
ctx, err := h.auth.ValidateSession(d.User, d.SID)
|
||||
if err != nil {
|
||||
logger.Warningf("invalid session: %v", err)
|
||||
ClearSession(w)
|
||||
return nil, trace.Wrap(teleport.AccessDenied("need auth"))
|
||||
}
|
||||
if creds.Password != ctx.GetWebSession().WS.BearerToken {
|
||||
logger.Warningf("bad bearer token")
|
||||
return nil, trace.Wrap(teleport.AccessDenied("bad bearer token"))
|
||||
if checkBearerToken {
|
||||
creds, err := roundtrip.ParseAuthHeaders(r)
|
||||
if err != nil {
|
||||
logger.Warningf("no auth headers %v", err)
|
||||
return nil, trace.Wrap(teleport.AccessDenied("need auth"))
|
||||
}
|
||||
if creds.Password != ctx.GetWebSession().WS.BearerToken {
|
||||
logger.Warningf("bad bearer token")
|
||||
return nil, trace.Wrap(teleport.AccessDenied("bad bearer token"))
|
||||
}
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
</head>
|
||||
<body class="grv">
|
||||
<div id="app"></div>
|
||||
<div id="bearer_token" style="display: none;">{"session": {{.Session}} }</div>
|
||||
</body>
|
||||
<script src="/web/app/app.js?ver=[VERSION]"></script>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue