Merge pull request #6 from SeanOC/master

Added the ability to pass an optional redirect path as part of the manual login form
This commit is contained in:
Jehiah Czebotar 2013-10-23 13:36:27 -07:00
commit 9a6e895368
2 changed files with 38 additions and 17 deletions

View File

@ -67,13 +67,16 @@ func (p *OauthProxy) SetRedirectUrl(redirectUrl *url.URL) {
p.redirectUrl = redirectUrl p.redirectUrl = redirectUrl
} }
func (p *OauthProxy) GetLoginURL() string { func (p *OauthProxy) GetLoginURL(redirectUrl string) string {
params := url.Values{} params := url.Values{}
params.Add("redirect_uri", p.redirectUrl.String()) params.Add("redirect_uri", p.redirectUrl.String())
params.Add("approval_prompt", "force") params.Add("approval_prompt", "force")
params.Add("scope", p.oauthScope) params.Add("scope", p.oauthScope)
params.Add("client_id", p.clientID) params.Add("client_id", p.clientID)
params.Add("response_type", "code") params.Add("response_type", "code")
if strings.HasPrefix(redirectUrl, "/") {
params.Add("state", redirectUrl)
}
return fmt.Sprintf("%s?%s", p.oauthLoginUrl, params.Encode()) return fmt.Sprintf("%s?%s", p.oauthLoginUrl, params.Encode())
} }
@ -100,6 +103,9 @@ func apiRequest(req *http.Request) (*simplejson.Json, error) {
} }
func (p *OauthProxy) redeemCode(code string) (string, error) { func (p *OauthProxy) redeemCode(code string) (string, error) {
if code == "" {
return "", errors.New("missing code")
}
params := url.Values{} params := url.Values{}
params.Add("redirect_uri", p.redirectUrl.String()) params.Add("redirect_uri", p.redirectUrl.String())
params.Add("client_id", p.clientID) params.Add("client_id", p.clientID)
@ -197,7 +203,6 @@ func (p *OauthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, m
} }
func (p *OauthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code int) { func (p *OauthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code int) {
// TODO: capture state for which url to redirect to at the end
p.ClearCookie(rw, req) p.ClearCookie(rw, req)
rw.WriteHeader(code) rw.WriteHeader(code)
templates := getTemplates() templates := getTemplates()
@ -205,9 +210,11 @@ func (p *OauthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
t := struct { t := struct {
SignInMessage string SignInMessage string
Htpasswd bool Htpasswd bool
Redirect string
}{ }{
SignInMessage: p.SignInMessage, SignInMessage: p.SignInMessage,
Htpasswd: p.HtpasswdFile != nil, Htpasswd: p.HtpasswdFile != nil,
Redirect: req.URL.RequestURI(),
} }
templates.ExecuteTemplate(rw, "sign_in.html", t) templates.ExecuteTemplate(rw, "sign_in.html", t)
} }
@ -239,39 +246,46 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
var ok bool var ok bool
var user string var user string
redirect := req.FormValue("rd")
if redirect == "" {
redirect = "/"
}
if req.URL.Path == signInPath { if req.URL.Path == signInPath {
user, ok = p.ManualSignIn(rw, req) user, ok = p.ManualSignIn(rw, req)
if ok { if ok {
p.SetCookie(rw, req, user) p.SetCookie(rw, req, user)
http.Redirect(rw, req, "/", 302) http.Redirect(rw, req, redirect, 302)
} else { } else {
p.SignInPage(rw, req, 200) p.SignInPage(rw, req, 200)
} }
return return
} }
if req.URL.Path == oauthStartPath { if req.URL.Path == oauthStartPath {
http.Redirect(rw, req, p.GetLoginURL(), 302) // get the ?rd= value
return err := req.ParseForm()
}
if req.URL.Path == oauthCallbackPath {
// finish the oauth cycle
reqParams, err := url.ParseQuery(req.URL.RawQuery)
if err != nil { if err != nil {
p.ErrorPage(rw, 500, "Internal Error", err.Error()) p.ErrorPage(rw, 500, "Internal Error", err.Error())
return return
} }
errorString, ok := reqParams["error"] http.Redirect(rw, req, p.GetLoginURL(redirect), 302)
if ok && len(errorString) == 1 {
p.ErrorPage(rw, 403, "Permission Denied", errorString[0])
return return
} }
code, ok := reqParams["code"] if req.URL.Path == oauthCallbackPath {
if !ok || len(code) != 1 { // finish the oauth cycle
p.ErrorPage(rw, 500, "Internal Error", "Invalid API response") err := req.ParseForm()
if err != nil {
p.ErrorPage(rw, 500, "Internal Error", err.Error())
return
}
errorString := req.Form.Get("error")
if errorString != "" {
p.ErrorPage(rw, 403, "Permission Denied", errorString)
return return
} }
token, err := p.redeemCode(code[0]) token, err := p.redeemCode(req.Form.Get("code"))
if err != nil { if err != nil {
log.Printf("error redeeming code %s", err.Error()) log.Printf("error redeeming code %s", err.Error())
p.ErrorPage(rw, 500, "Internal Error", err.Error()) p.ErrorPage(rw, 500, "Internal Error", err.Error())
@ -285,11 +299,16 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return return
} }
redirect := req.Form.Get("state")
if redirect == "" {
redirect = "/"
}
// set cookie, or deny // set cookie, or deny
if p.Validator(email) { if p.Validator(email) {
log.Printf("authenticating %s completed", email) log.Printf("authenticating %s completed", email)
p.SetCookie(rw, req, email) p.SetCookie(rw, req, email)
http.Redirect(rw, req, "/", 302) http.Redirect(rw, req, redirect, 302)
return return
} else { } else {
p.ErrorPage(rw, 403, "Permission Denied", "Invalid Account") p.ErrorPage(rw, 403, "Permission Denied", "Invalid Account")

View File

@ -12,12 +12,14 @@ func getTemplates() *template.Template {
<head><title>Sign In</title></head> <head><title>Sign In</title></head>
<body> <body>
<form method="GET" action="/oauth2/start"> <form method="GET" action="/oauth2/start">
<input type="hidden" name="rd" value="{{.Redirect}}">
<button type="submit">Sign In w/ Google</button> <button type="submit">Sign In w/ Google</button>
{{.SignInMessage}} {{.SignInMessage}}
</form> </form>
{{ if .Htpasswd }} {{ if .Htpasswd }}
<fieldset> <fieldset>
<form method="POST" action="/oauth2/sign_in"> <form method="POST" action="/oauth2/sign_in">
<input type="hidden" name="rd" value="{{.Redirect}}">
<label>Username: <input type="text" name="username" size="10"></label><br/> <label>Username: <input type="text" name="username" size="10"></label><br/>
<label>Password: <input type="password" name="password" size="10"></label><br/> <label>Password: <input type="password" name="password" size="10"></label><br/>
<button type="submit">Sign In</button> <button type="submit">Sign In</button>