preliminary implemntation of redirect logic

This commit is contained in:
luke.hopkins
2017-11-17 17:43:53 +00:00
parent f3c6fb3c66
commit dbba63f98e
2 changed files with 133 additions and 37 deletions
+42 -32
View File
@@ -23,20 +23,23 @@ func init() {
// DefaultConfig for the loginsrv handler
func DefaultConfig() *Config {
return &Config{
Host: "localhost",
Port: "6789",
LogLevel: "info",
JwtSecret: jwtDefaultSecret,
JwtExpiry: 24 * time.Hour,
JwtRefreshes: 0,
SuccessURL: "/",
LogoutURL: "",
LoginPath: "/login",
CookieName: "jwt_token",
CookieHTTPOnly: true,
Backends: Options{},
Oauth: Options{},
GracePeriod: 5 * time.Second,
Host: "localhost",
Port: "6789",
LogLevel: "info",
JwtSecret: jwtDefaultSecret,
JwtExpiry: 24 * time.Hour,
JwtRefreshes: 0,
SuccessURL: "/",
AllowRedirects: true,
PreventExternalRedirects: true,
CheckRefererOnRedirects: true,
LogoutURL: "",
LoginPath: "/login",
CookieName: "jwt_token",
CookieHTTPOnly: true,
Backends: Options{},
Oauth: Options{},
GracePeriod: 5 * time.Second,
}
}
@@ -44,24 +47,27 @@ const envPrefix = "LOGINSRV_"
// Config for the loginsrv handler
type Config struct {
Host string
Port string
LogLevel string
TextLogging bool
JwtSecret string
JwtExpiry time.Duration
JwtRefreshes int
SuccessURL string
LogoutURL string
Template string
LoginPath string
CookieName string
CookieExpiry time.Duration
CookieDomain string
CookieHTTPOnly bool
Backends Options
Oauth Options
GracePeriod time.Duration
Host string
Port string
LogLevel string
TextLogging bool
JwtSecret string
JwtExpiry time.Duration
JwtRefreshes int
SuccessURL string
AllowRedirects bool
PreventExternalRedirects bool
CheckRefererOnRedirects bool
LogoutURL string
Template string
LoginPath string
CookieName string
CookieExpiry time.Duration
CookieDomain string
CookieHTTPOnly bool
Backends Options
Oauth Options
GracePeriod time.Duration
}
// Options is the configuration structure for oauth and backend provider
@@ -104,6 +110,10 @@ func (c *Config) ConfigureFlagSet(f *flag.FlagSet) {
f.DurationVar(&c.CookieExpiry, "cookie-expiry", c.CookieExpiry, "The expiry duration for the cookie, e.g. 2h or 3h30m. Default is browser session")
f.StringVar(&c.CookieDomain, "cookie-domain", c.CookieDomain, "The optional domain parameter for the cookie")
f.StringVar(&c.SuccessURL, "success-url", c.SuccessURL, "The url to redirect after login")
f.BoolVar(&c.AllowRedirects, "allow-redirects", c.AllowRedirects, "Allow dynamic redirects by parameter")
f.BoolVar(&c.PreventExternalRedirects, "prevent-external-redirects", c.PreventExternalRedirects, "Prevent dynamic redirects from redirecting to an external domain")
f.BoolVar(&c.CheckRefererOnRedirects, "check-referer-on-redirects", c.CheckRefererOnRedirects, "When redirecting check that the referer is the same domain")
f.StringVar(&c.LogoutURL, "logout-url", c.LogoutURL, "The url or path to redirect after logout")
f.StringVar(&c.Template, "template", c.Template, "An alternative template for the login form")
f.StringVar(&c.LoginPath, "login-path", c.LoginPath, "The path of the login resource")
+91 -5
View File
@@ -4,14 +4,17 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/dgrijalva/jwt-go"
"github.com/tarent/loginsrv/logging"
"github.com/tarent/loginsrv/model"
"github.com/tarent/loginsrv/oauth2"
"io/ioutil"
"net/http"
"strings"
"time"
)
const contentTypeHTML = "text/html; charset=utf-8"
@@ -71,11 +74,34 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.handleOauth(w, r)
return
}
if h.shouldSetCookie(r) {
queries, _ := url.ParseQuery(r.URL.RawQuery)
cookie := http.Cookie{Name: "redirect_url", Value: queries.Get("backTo")}
http.SetCookie(w, &cookie)
}
h.handleLogin(w, r)
return
}
func (h *Handler) shouldSetCookie(r *http.Request) bool {
if h.config.AllowRedirects {
if h.config.CheckRefererOnRedirects {
referer, _ := url.Parse(r.Header.Get("Referer"))
if referer.Host != r.Host {
logging.Application(r.Header).Warnf(
"Referer domain: '%s' does not match current domain '%s'",
referer.Host,
r.Host,
)
return false
}
}
return true
}
return false
}
func (h *Handler) handleOauth(w http.ResponseWriter, r *http.Request) {
startedFlow, authenticated, userInfo, err := h.oauth.Handle(w, r)
@@ -231,7 +257,10 @@ func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, u
http.SetCookie(w, cookie)
w.Header().Set("Location", h.config.SuccessURL)
//redirectURL := h.config.SuccessURL
//fmt.Printf("redirectURL is: %s\n", h.redirectURL(r))
w.Header().Set("Location", h.redirectURL(r))
w.WriteHeader(303)
return
}
@@ -241,6 +270,63 @@ func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, u
fmt.Fprintf(w, "%s", token)
}
func (h *Handler) redirectURL(r *http.Request) string {
origin, _ := url.Parse(r.Header.Get("Origin"))
originScheme := origin.Scheme
originHost := origin.Host
redirectURL := fmt.Sprintf("%s://%s%s", originScheme, originHost, h.config.SuccessURL)
if h.config.AllowRedirects {
cookie, _ := r.Cookie("redirect_url")
parsedURL, err := url.Parse(cookie.Value)
if err != nil {
spew.Dump("oops can't parse that url")
}
scheme := parsedURL.Scheme
host := parsedURL.Host
path := parsedURL.Path
//spew.Dump(scheme)
//spew.Dump(host)
//spew.Dump(path)
if scheme == "" {
scheme = originScheme
}
if host == "" {
host = originHost
} else {
if h.config.PreventExternalRedirects {
if host != originHost {
logging.Application(r.Header).Warnf(
"Redirect attempt to '%s' but -prevent-external-redirects is set to true",
host,
)
return redirectURL
} else {
host = originHost
}
} else {
//TODO read domains from file
domains := "2google.com"
if host != domains {
logging.Application(r.Header).Warnf(
"Redirect attempt to '%s' but it is not in domain whitelist",
host,
)
return redirectURL
}
}
}
if path == "" {
path = h.config.SuccessURL
}
redirectURL = fmt.Sprintf("%s://%s%s", scheme, host, path)
}
return redirectURL
}
func (h *Handler) createToken(userInfo jwt.Claims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS512, userInfo)
return token.SignedString([]byte(h.config.JwtSecret))