auth
19:9fe684b33b3d Browse Files
Implement session management and move login. Add a session store interface to validate and retrieve data about sessions. Implement session management in endpoints that need session support. Move the login functionality from inlined into the OAuth flow into its own handler, that the OAuth flow will redirect to. The login functionality should take a redirect URL parameter to return to the OAuth flow when login is completed.
access.go authorize.go config.go context.go errors.go session.go storage.go
1.1 --- a/access.go Sat Aug 16 18:05:55 2014 -0400 1.2 +++ b/access.go Sat Aug 16 20:02:52 2014 -0400 1.3 @@ -251,7 +251,7 @@ 1.4 1.5 _, err = ctx.Profiles.GetProfile(username, password) 1.6 if err != nil { 1.7 - if err == ProfileNotFoundError { 1.8 + if err == ErrProfileNotFound { 1.9 ctx.RenderJSONError(w, ErrorInvalidGrant, "Invalid credentials.", ctx.Config.DocumentationDomain) 1.10 return 1.11 }
2.1 --- a/authorize.go Sat Aug 16 18:05:55 2014 -0400 2.2 +++ b/authorize.go Sat Aug 16 20:02:52 2014 -0400 2.3 @@ -148,7 +148,7 @@ 2.4 } 2.5 2.6 if err := validateSession(r, ctx); err == ErrorNotAuthenticated { 2.7 - ctx.RenderLogin(w, r) 2.8 + // TODO: redirect to login 2.9 return 2.10 } else if err != nil { 2.11 ctx.RenderError(w, err) 2.12 @@ -201,7 +201,7 @@ 2.13 } 2.14 2.15 if err := validateSession(r, ctx); err == ErrorNotAuthenticated { 2.16 - ctx.RenderLogin(w, r) 2.17 + // TODO: redirect to login 2.18 return 2.19 } else if err != nil { 2.20 ctx.RenderError(w, err)
3.1 --- a/config.go Sat Aug 16 18:05:55 2014 -0400 3.2 +++ b/config.go Sat Aug 16 20:02:52 2014 -0400 3.3 @@ -1,5 +1,7 @@ 3.4 package auth 3.5 3.6 +import "time" 3.7 + 3.8 // AllowedAuthorizeType is a collection of allowed auth request types 3.9 type AllowedAuthorizeType []AuthorizeRequestType 3.10 3.11 @@ -56,6 +58,9 @@ 3.12 3.13 // The base path of documentation 3.14 DocumentationDomain string 3.15 + 3.16 + SessionLength time.Duration 3.17 + RequestIPHeader string 3.18 } 3.19 3.20 // NewServerConfig returns a new ServerConfig with default configuration
4.1 --- a/context.go Sat Aug 16 18:05:55 2014 -0400 4.2 +++ b/context.go Sat Aug 16 20:02:52 2014 -0400 4.3 @@ -15,6 +15,7 @@ 4.4 Clients ClientStore 4.5 Tokens TokenStore 4.6 Profiles ProfileStore 4.7 + Sessions SessionStore 4.8 Log *log.Logger 4.9 Templates Templates 4.10 }
5.1 --- a/errors.go Sat Aug 16 18:05:55 2014 -0400 5.2 +++ b/errors.go Sat Aug 16 20:02:52 2014 -0400 5.3 @@ -21,7 +21,7 @@ 5.4 ErrorNotAuthenticated = errors.New("Not authenticated.") 5.5 InvalidClientError = errors.New("Invalid client.") 5.6 AuthorizationNotFoundError = errors.New("Authorization not found.") 5.7 - ProfileNotFoundError = errors.New("Profile not found.") 5.8 + ErrProfileNotFound = errors.New("Profile not found.") 5.9 TokenNotFoundError = errors.New("Token not found.") 5.10 NilClientError = errors.New("Client was nil.") 5.11 )
6.1 --- a/session.go Sat Aug 16 18:05:55 2014 -0400 6.2 +++ b/session.go Sat Aug 16 20:02:52 2014 -0400 6.3 @@ -1,8 +1,76 @@ 6.4 package auth 6.5 6.6 -import "net/http" 6.7 +import ( 6.8 + "errors" 6.9 + "net/http" 6.10 + "time" 6.11 + 6.12 + "secondbit.org/uuid" 6.13 +) 6.14 + 6.15 +const sessionCookie = "session" 6.16 + 6.17 +var ( 6.18 + ErrSessionNotFound = errors.New("Session not found.") 6.19 +) 6.20 + 6.21 +type Session struct { 6.22 + Token string 6.23 + User uuid.ID 6.24 + Expires time.Time 6.25 + Created time.Time 6.26 + IP string 6.27 +} 6.28 6.29 func validateSession(r *http.Request, c Context) error { 6.30 - // TODO: return an error if the user does not have a valid session 6.31 - return nil 6.32 + cookie, err := r.Cookie(sessionCookie) 6.33 + if err == http.ErrNoCookie { 6.34 + return ErrSessionNotFound 6.35 + } 6.36 + _, err = c.Sessions.GetSession(cookie.Value) 6.37 + return err 6.38 } 6.39 + 6.40 +func HandleLoginRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 6.41 + if r.Method == "GET" { 6.42 + ctx.RenderLogin(w, r) 6.43 + return 6.44 + } else if r.Method != "POST" { 6.45 + // TODO: return bad method error 6.46 + return 6.47 + } 6.48 + 6.49 + if r.FormValue("username") == "" || r.FormValue("password") == "" { 6.50 + // TODO: return unauthenticated error 6.51 + return 6.52 + } 6.53 + id, err := ctx.Profiles.GetProfile(r.FormValue("username"), r.FormValue("password")) 6.54 + if err != nil { 6.55 + if err == ErrProfileNotFound { 6.56 + // TODO: return unauthenticated error 6.57 + return 6.58 + } 6.59 + // TODO: return internal server error 6.60 + return 6.61 + } 6.62 + session := Session{ 6.63 + Token: newToken(), 6.64 + User: id, 6.65 + Expires: time.Now().Add(ctx.Config.SessionLength), 6.66 + Created: time.Now(), 6.67 + IP: r.Header.Get(ctx.Config.RequestIPHeader), 6.68 + } 6.69 + err = ctx.Sessions.SetSession(session) 6.70 + if err != nil { 6.71 + // TODO: return internal server error 6.72 + return 6.73 + } 6.74 + http.SetCookie(w, &http.Cookie{ 6.75 + Name: sessionCookie, 6.76 + Value: session.Token, 6.77 + Expires: session.Expires, 6.78 + Secure: true, 6.79 + HttpOnly: true, 6.80 + }) 6.81 + // TODO: redirect 6.82 +}
7.1 --- a/storage.go Sat Aug 16 18:05:55 2014 -0400 7.2 +++ b/storage.go Sat Aug 16 20:02:52 2014 -0400 7.3 @@ -26,3 +26,10 @@ 7.4 type ProfileStore interface { 7.5 GetProfile(username, password string) (uuid.ID, error) 7.6 } 7.7 + 7.8 +type SessionStore interface { 7.9 + GetSession(token string) (Session, error) 7.10 + GetAllSessions(username string) ([]Session, error) 7.11 + SetSession(Session) error 7.12 + ExpireSession(token string) error 7.13 +}