auth
2014-08-16
Parent:9fe684b33b3d
auth/session.go
Redirect after login. After a successful login, redirect based on a query parameter. Only allow redirections to the domain listed in the config and its subdomains. If no redirect is specified, redirect to the root of the domain listed in the config.
| paddy@6 | 1 package auth |
| paddy@1 | 2 |
| paddy@19 | 3 import ( |
| paddy@19 | 4 "errors" |
| paddy@19 | 5 "net/http" |
| paddy@21 | 6 "net/url" |
| paddy@19 | 7 "time" |
| paddy@19 | 8 |
| paddy@21 | 9 "strings" |
| paddy@19 | 10 "secondbit.org/uuid" |
| paddy@19 | 11 ) |
| paddy@19 | 12 |
| paddy@19 | 13 const sessionCookie = "session" |
| paddy@19 | 14 |
| paddy@19 | 15 var ( |
| paddy@19 | 16 ErrSessionNotFound = errors.New("Session not found.") |
| paddy@19 | 17 ) |
| paddy@19 | 18 |
| paddy@19 | 19 type Session struct { |
| paddy@19 | 20 Token string |
| paddy@19 | 21 User uuid.ID |
| paddy@19 | 22 Expires time.Time |
| paddy@19 | 23 Created time.Time |
| paddy@19 | 24 IP string |
| paddy@19 | 25 } |
| paddy@1 | 26 |
| paddy@2 | 27 func validateSession(r *http.Request, c Context) error { |
| paddy@19 | 28 cookie, err := r.Cookie(sessionCookie) |
| paddy@19 | 29 if err == http.ErrNoCookie { |
| paddy@19 | 30 return ErrSessionNotFound |
| paddy@19 | 31 } |
| paddy@19 | 32 _, err = c.Sessions.GetSession(cookie.Value) |
| paddy@19 | 33 return err |
| paddy@1 | 34 } |
| paddy@19 | 35 |
| paddy@19 | 36 func HandleLoginRequest(w http.ResponseWriter, r *http.Request, ctx Context) { |
| paddy@19 | 37 if r.Method == "GET" { |
| paddy@19 | 38 ctx.RenderLogin(w, r) |
| paddy@19 | 39 return |
| paddy@19 | 40 } else if r.Method != "POST" { |
| paddy@19 | 41 // TODO: return bad method error |
| paddy@19 | 42 return |
| paddy@19 | 43 } |
| paddy@19 | 44 |
| paddy@19 | 45 if r.FormValue("username") == "" || r.FormValue("password") == "" { |
| paddy@19 | 46 // TODO: return unauthenticated error |
| paddy@19 | 47 return |
| paddy@19 | 48 } |
| paddy@19 | 49 id, err := ctx.Profiles.GetProfile(r.FormValue("username"), r.FormValue("password")) |
| paddy@19 | 50 if err != nil { |
| paddy@19 | 51 if err == ErrProfileNotFound { |
| paddy@19 | 52 // TODO: return unauthenticated error |
| paddy@19 | 53 return |
| paddy@19 | 54 } |
| paddy@19 | 55 // TODO: return internal server error |
| paddy@19 | 56 return |
| paddy@19 | 57 } |
| paddy@19 | 58 session := Session{ |
| paddy@19 | 59 Token: newToken(), |
| paddy@19 | 60 User: id, |
| paddy@19 | 61 Expires: time.Now().Add(ctx.Config.SessionLength), |
| paddy@19 | 62 Created: time.Now(), |
| paddy@19 | 63 IP: r.Header.Get(ctx.Config.RequestIPHeader), |
| paddy@19 | 64 } |
| paddy@19 | 65 err = ctx.Sessions.SetSession(session) |
| paddy@19 | 66 if err != nil { |
| paddy@19 | 67 // TODO: return internal server error |
| paddy@19 | 68 return |
| paddy@19 | 69 } |
| paddy@19 | 70 http.SetCookie(w, &http.Cookie{ |
| paddy@19 | 71 Name: sessionCookie, |
| paddy@19 | 72 Value: session.Token, |
| paddy@19 | 73 Expires: session.Expires, |
| paddy@19 | 74 Secure: true, |
| paddy@19 | 75 HttpOnly: true, |
| paddy@19 | 76 }) |
| paddy@21 | 77 |
| paddy@21 | 78 redirectString := r.URL.Query().Get("redirect_to") |
| paddy@21 | 79 if redirectString != "" { |
| paddy@21 | 80 redirectURI, err := url.Parse(redirectString) |
| paddy@21 | 81 if err != nil { |
| paddy@21 | 82 // TODO: render a bad request error |
| paddy@21 | 83 return |
| paddy@21 | 84 } |
| paddy@21 | 85 if !strings.HasSuffix("."+ctx.Config.LoginRedirectDomain, redirectURI.Host) && redirectURI.Host != ctx.Config.LoginRedirectDomain { |
| paddy@21 | 86 // TODO: render a bad request error |
| paddy@21 | 87 return |
| paddy@21 | 88 } |
| paddy@21 | 89 } else { |
| paddy@21 | 90 redirectString = "https://" + ctx.Config.LoginRedirectDomain |
| paddy@21 | 91 } |
| paddy@21 | 92 http.Redirect(w, r, redirectString, http.StatusFound) |
| paddy@21 | 93 return |
| paddy@19 | 94 } |