Add remember me functionality, handle both types of sessions appropriately
This commit is contained in:
		@@ -24,13 +24,14 @@ func (postController *PostController) Login(w http.ResponseWriter, r *http.Reque
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	username := r.FormValue("username")
 | 
						username := r.FormValue("username")
 | 
				
			||||||
	password := r.FormValue("password")
 | 
						password := r.FormValue("password")
 | 
				
			||||||
 | 
						remember := r.FormValue("remember") == "on"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if username == "" || password == "" {
 | 
						if username == "" || password == "" {
 | 
				
			||||||
		log.Println("Tried to login user with empty username or password")
 | 
							log.Println("Tried to login user with empty username or password")
 | 
				
			||||||
		http.Redirect(w, r, "/login", http.StatusFound)
 | 
							http.Redirect(w, r, "/login", http.StatusFound)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = models.AuthenticateUser(postController.App, w, username, password)
 | 
						_, err = models.AuthenticateUser(postController.App, w, username, password, remember)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Println("Error authenticating user")
 | 
							log.Println("Error authenticating user")
 | 
				
			||||||
		log.Println(err)
 | 
							log.Println(err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ func RunAllMigrations(app *app.App) error {
 | 
				
			|||||||
		Id:         1,
 | 
							Id:         1,
 | 
				
			||||||
		UserId:     1,
 | 
							UserId:     1,
 | 
				
			||||||
		AuthToken:  "migrate",
 | 
							AuthToken:  "migrate",
 | 
				
			||||||
 | 
							RememberMe: false,
 | 
				
			||||||
		CreatedAt:  time.Now(),
 | 
							CreatedAt:  time.Now(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = database.Migrate(app, session)
 | 
						err = database.Migrate(app, session)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ type Session struct {
 | 
				
			|||||||
	Id         int64
 | 
						Id         int64
 | 
				
			||||||
	UserId     int64
 | 
						UserId     int64
 | 
				
			||||||
	AuthToken  string
 | 
						AuthToken  string
 | 
				
			||||||
 | 
						RememberMe bool
 | 
				
			||||||
	CreatedAt  time.Time
 | 
						CreatedAt  time.Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,13 +27,15 @@ const (
 | 
				
			|||||||
	insertSession                 = "INSERT INTO " + sessionTable + " (" + sessionColumnsNoId + ") VALUES ($1, $2, $3) RETURNING \"Id\""
 | 
						insertSession                 = "INSERT INTO " + sessionTable + " (" + sessionColumnsNoId + ") VALUES ($1, $2, $3) RETURNING \"Id\""
 | 
				
			||||||
	deleteSessionByAuthToken      = "DELETE FROM " + sessionTable + " WHERE \"AuthToken\" = $1"
 | 
						deleteSessionByAuthToken      = "DELETE FROM " + sessionTable + " WHERE \"AuthToken\" = $1"
 | 
				
			||||||
	deleteSessionsOlderThan30Days = "DELETE FROM " + sessionTable + " WHERE \"CreatedAt\" < NOW() - INTERVAL '30 days'"
 | 
						deleteSessionsOlderThan30Days = "DELETE FROM " + sessionTable + " WHERE \"CreatedAt\" < NOW() - INTERVAL '30 days'"
 | 
				
			||||||
 | 
						deleteSessionsOlderThan6Hours = "DELETE FROM " + sessionTable + " WHERE \"CreatedAt\" < NOW() - INTERVAL '6 hours' AND \"RememberMe\" = false"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreateSession creates a new session for a user
 | 
					// CreateSession creates a new session for a user
 | 
				
			||||||
func CreateSession(app *app.App, w http.ResponseWriter, userId int64) (Session, error) {
 | 
					func CreateSession(app *app.App, w http.ResponseWriter, userId int64, remember bool) (Session, error) {
 | 
				
			||||||
	session := Session{}
 | 
						session := Session{}
 | 
				
			||||||
	session.UserId = userId
 | 
						session.UserId = userId
 | 
				
			||||||
	session.AuthToken = generateAuthToken(app)
 | 
						session.AuthToken = generateAuthToken(app)
 | 
				
			||||||
 | 
						session.RememberMe = remember
 | 
				
			||||||
	session.CreatedAt = time.Now()
 | 
						session.CreatedAt = time.Now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If the AuthToken column for any user matches the token, set existingAuthToken to true
 | 
						// If the AuthToken column for any user matches the token, set existingAuthToken to true
 | 
				
			||||||
@@ -47,11 +50,11 @@ func CreateSession(app *app.App, w http.ResponseWriter, userId int64) (Session,
 | 
				
			|||||||
	// If duplicate token found, recursively call function until unique token is generated
 | 
						// If duplicate token found, recursively call function until unique token is generated
 | 
				
			||||||
	if existingAuthToken == true {
 | 
						if existingAuthToken == true {
 | 
				
			||||||
		log.Println("Duplicate token found in sessions table, generating new token...")
 | 
							log.Println("Duplicate token found in sessions table, generating new token...")
 | 
				
			||||||
		return CreateSession(app, w, userId)
 | 
							return CreateSession(app, w, userId, remember)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Insert session into database
 | 
						// Insert session into database
 | 
				
			||||||
	err = app.Db.QueryRow(insertSession, session.UserId, session.AuthToken, session.CreatedAt).Scan(&session.Id)
 | 
						err = app.Db.QueryRow(insertSession, session.UserId, session.AuthToken, session.RememberMe, session.CreatedAt).Scan(&session.Id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Println("Error inserting session into database")
 | 
							log.Println("Error inserting session into database")
 | 
				
			||||||
		return Session{}, err
 | 
							return Session{}, err
 | 
				
			||||||
@@ -76,14 +79,26 @@ func generateAuthToken(app *app.App) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// createSessionCookie creates a new session cookie
 | 
					// createSessionCookie creates a new session cookie
 | 
				
			||||||
func createSessionCookie(app *app.App, w http.ResponseWriter, session Session) {
 | 
					func createSessionCookie(app *app.App, w http.ResponseWriter, session Session) {
 | 
				
			||||||
	cookie := &http.Cookie{
 | 
						cookie := &http.Cookie{}
 | 
				
			||||||
 | 
						if session.RememberMe {
 | 
				
			||||||
 | 
							cookie = &http.Cookie{
 | 
				
			||||||
			Name:     "session",
 | 
								Name:     "session",
 | 
				
			||||||
			Value:    session.AuthToken,
 | 
								Value:    session.AuthToken,
 | 
				
			||||||
			Path:     "/",
 | 
								Path:     "/",
 | 
				
			||||||
		MaxAge:   86400,
 | 
								MaxAge:   2592000 * 1000, // 30 days in ms
 | 
				
			||||||
			HttpOnly: true,
 | 
								HttpOnly: true,
 | 
				
			||||||
			Secure:   true,
 | 
								Secure:   true,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							cookie = &http.Cookie{
 | 
				
			||||||
 | 
								Name:     "session",
 | 
				
			||||||
 | 
								Value:    session.AuthToken,
 | 
				
			||||||
 | 
								Path:     "/",
 | 
				
			||||||
 | 
								MaxAge:   21600 * 1000, // 6 hours in ms
 | 
				
			||||||
 | 
								HttpOnly: true,
 | 
				
			||||||
 | 
								Secure:   true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	http.SetCookie(w, cookie)
 | 
						http.SetCookie(w, cookie)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -116,10 +131,17 @@ func DeleteSessionByAuthToken(app *app.App, w http.ResponseWriter, authToken str
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ScheduledSessionCleanup deletes expired sessions from the database
 | 
					// ScheduledSessionCleanup deletes expired sessions from the database
 | 
				
			||||||
func ScheduledSessionCleanup(app *app.App) {
 | 
					func ScheduledSessionCleanup(app *app.App) {
 | 
				
			||||||
	// Delete sessions older than 30 days
 | 
						// Delete sessions older than 30 days (remember me sessions)
 | 
				
			||||||
	_, err := app.Db.Exec(deleteSessionsOlderThan30Days)
 | 
						_, err := app.Db.Exec(deleteSessionsOlderThan30Days)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Println("Error deleting expired sessions from database")
 | 
							log.Println("Error deleting 30 day expired sessions from database")
 | 
				
			||||||
 | 
							log.Println(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete sessions older than 6 hours
 | 
				
			||||||
 | 
						_, err = app.Db.Exec(deleteSessionsOlderThan30Days)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Println("Error deleting 6 hour expired sessions from database")
 | 
				
			||||||
		log.Println(err)
 | 
							log.Println(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,7 +98,7 @@ func CreateUser(app *app.App, username string, password string, createdAt time.T
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AuthenticateUser validates the password for the specified user
 | 
					// AuthenticateUser validates the password for the specified user
 | 
				
			||||||
func AuthenticateUser(app *app.App, w http.ResponseWriter, username string, password string) (Session, error) {
 | 
					func AuthenticateUser(app *app.App, w http.ResponseWriter, username string, password string, remember bool) (Session, error) {
 | 
				
			||||||
	var user User
 | 
						var user User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Query row by username
 | 
						// Query row by username
 | 
				
			||||||
@@ -114,7 +114,7 @@ func AuthenticateUser(app *app.App, w http.ResponseWriter, username string, pass
 | 
				
			|||||||
		log.Println("Authentication error (incorrect password) for user:" + username)
 | 
							log.Println("Authentication error (incorrect password) for user:" + username)
 | 
				
			||||||
		return Session{}, err
 | 
							return Session{}, err
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		return CreateSession(app, w, user.Id)
 | 
							return CreateSession(app, w, user.Id, remember)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user