From 22a124cc6feb330a9884955c609e4a457fbefc0d Mon Sep 17 00:00:00 2001 From: ctiller15 Date: Tue, 28 Oct 2025 22:56:09 -0400 Subject: [PATCH 1/6] scaffold out post login function --- internal/api/handlers.go | 129 ++++++++++++++++++++++++++++++--- internal/api/handlers_test.go | 45 ++++++++++++ internal/database/users.sql.go | 35 +++++++++ main.go | 1 + sql/queries/users.sql | 8 +- 5 files changed, 207 insertions(+), 11 deletions(-) diff --git a/internal/api/handlers.go b/internal/api/handlers.go index bd7d095..aee42ba 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -21,9 +21,30 @@ type IndexPageData struct { BasePageData } +type SignupForm struct { + Email string + Password string + Valid bool +} + type SignupPageData struct { Title string - SignupDetails + SignupForm +} + +type LoginForm struct { + Email string + Password string + Valid bool +} + +type HtmlPageData interface { + SignupPageData | LoginPageData +} + +type LoginPageData struct { + Title string + LoginForm } type AttributionsPageData struct { @@ -82,22 +103,16 @@ func (a *APIConfig) HandleSignupPage(w http.ResponseWriter, r *http.Request) { } } -type SignupDetails struct { - Email string - Password string - Valid bool -} - func (a *APIConfig) HandlePostSignup(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - signupDetails := SignupDetails{ + signupDetails := SignupForm{ Email: r.FormValue("email"), Password: r.FormValue("password"), } signupPageData := SignupPageData{ - Title: "TailScribe - Sign Up", - SignupDetails: signupDetails, + Title: "TailScribe - Sign Up", + SignupForm: signupDetails, } tmpl := template.Must(template.ParseFiles( @@ -219,6 +234,100 @@ func expireCookie(w *http.ResponseWriter, cookie_name string) { }) } +func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + loginDetails := LoginForm{ + Email: r.FormValue("email"), + Password: r.FormValue("password"), + } + + loginPageData := LoginPageData{ + Title: "TailScribe - Log In", + LoginForm: loginDetails, + } + + tmpl := template.Must(template.ParseFiles( + "./templates/login.html", + "./templates/base.html", + )) + + // Hash password. + hashedPassword, err := auth.HashPassword(loginDetails.Password) + if err != nil { + loginDetails.Valid = false + w.WriteHeader(http.StatusUnauthorized) + err = tmpl.Execute(w, loginPageData) + if err != nil { + log.Fatal(err) + } + return + } + + // Retrieve user + getUserParams := database.GetUserByLoginCredentialsParams{ + Email: sql.NullString{ + String: loginDetails.Email, + Valid: true, + }, + Password: sql.NullString{ + String: hashedPassword, + Valid: true, + }, + } + + user, err := a.Db.GetUserByLoginCredentials(ctx, getUserParams) + if err != nil { + loginDetails.Valid = false + w.WriteHeader(http.StatusUnauthorized) + err = tmpl.Execute(w, loginPageData) + if err != nil { + log.Fatal(err) + } + } + + tokenString, err := auth.MakeJWT(user.ID, a.Env.Secret) + if err != nil { + loginDetails.Valid = false + w.WriteHeader(http.StatusInternalServerError) + err = tmpl.Execute(w, loginPageData) + if err != nil { + log.Fatal(err) + } + return + } + + refreshTokenString, err := auth.MakeRefreshToken() + if err != nil { + loginDetails.Valid = false + w.WriteHeader(http.StatusInternalServerError) + err = tmpl.Execute(w, loginPageData) + if err != nil { + log.Fatal(err) + } + return + } + + http.SetCookie(w, &http.Cookie{ + Name: "token", + Value: tokenString, + Expires: time.Now().Add(time.Hour * 24), + HttpOnly: true, + Secure: true, + Domain: "/", + SameSite: http.SameSiteStrictMode, + }) + http.SetCookie(w, &http.Cookie{ + Name: "refresh_token", + Value: refreshTokenString, + Expires: time.Now().Add(time.Hour * 30 * 24), + HttpOnly: true, + Domain: "/", + SameSite: http.SameSiteStrictMode, + }) + + http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect) +} + func (a *APIConfig) HandlePostLogout(w http.ResponseWriter, r *http.Request) { expireCookie(&w, "token") expireCookie(&w, "refresh_token") diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go index 0956cc5..202f7f0 100644 --- a/internal/api/handlers_test.go +++ b/internal/api/handlers_test.go @@ -168,6 +168,51 @@ func TestHandleLogout(t *testing.T) { }) } +func TestHandlePostLogin(t *testing.T) { + t.Run("Successfully logs a user in", func(t *testing.T) { + formData := url.Values{ + "email": {"testEmail2@email.com"}, + "password": {"password123"}, + } + + request, _ := http.NewRequest(http.MethodPost, "/signup", strings.NewReader(formData.Encode())) + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + response := httptest.NewRecorder() + apiCfg := NewAPIConfig(TestEnvVars, DbQueries) + apiCfg.HandlePostSignup(response, request) + + result := response.Result() + assert.Equal(t, 201, result.StatusCode) + + loginFormData := url.Values{ + "email": {"testEmail2@email.com"}, + "password": {"password123"}, + } + + loginRequest, _ := http.NewRequest(http.MethodPost, "/login", strings.NewReader(loginFormData.Encode())) + loginRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + loginResponse := httptest.NewRecorder() + loginApiCfg := NewAPIConfig(TestEnvVars, DbQueries) + loginApiCfg.HandlePostLogin(loginResponse, loginRequest) + + loginResult := response.Result() + assert.Equal(t, 302, result.StatusCode) + + cookies := loginResult.Cookies() + assert.NotNil(t, cookies[0]) + assert.Equal(t, "token", cookies[0].Name) + assert.NotNil(t, cookies[1]) + assert.Equal(t, "refresh_token", cookies[1].Name) + t.Errorf("Finish the test!") + }) + + t.Run("Fails to log a user in if invalid username/password", func(t *testing.T) { + t.Errorf("Finish the test!") + }) + t.Errorf("Finish the test!") +} + func TestGetAttributions(t *testing.T) { request, _ := http.NewRequest(http.MethodGet, "/attributions", nil) response := httptest.NewRecorder() diff --git a/internal/database/users.sql.go b/internal/database/users.sql.go index 5fcc529..577d843 100644 --- a/internal/database/users.sql.go +++ b/internal/database/users.sql.go @@ -57,3 +57,38 @@ func (q *Queries) DeleteUsers(ctx context.Context) error { _, err := q.db.ExecContext(ctx, deleteUsers) return err } + +const getUserByLoginCredentials = `-- name: GetUserByLoginCredentials :one +SELECT id, email, username, firstname, lastname, password, facebook_id, reset_password_token, reset_password_expires, is_premium, premium_level, stripe_customer_id, is_deleted, created_at, updated_at +FROM users +WHERE email = $1 +AND password = $2 +` + +type GetUserByLoginCredentialsParams struct { + Email sql.NullString + Password sql.NullString +} + +func (q *Queries) GetUserByLoginCredentials(ctx context.Context, arg GetUserByLoginCredentialsParams) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByLoginCredentials, arg.Email, arg.Password) + var i User + err := row.Scan( + &i.ID, + &i.Email, + &i.Username, + &i.Firstname, + &i.Lastname, + &i.Password, + &i.FacebookID, + &i.ResetPasswordToken, + &i.ResetPasswordExpires, + &i.IsPremium, + &i.PremiumLevel, + &i.StripeCustomerID, + &i.IsDeleted, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/main.go b/main.go index 5531f54..a4d6d29 100644 --- a/main.go +++ b/main.go @@ -42,6 +42,7 @@ func main() { mux.HandleFunc("/", apiCfg.HandleIndex) mux.HandleFunc("GET /signup", apiCfg.HandleSignupPage) mux.HandleFunc("POST /signup", apiCfg.HandlePostSignup) + mux.HandleFunc("POST /login", apiCfg.HandlePostLogin) mux.HandleFunc("POST /logout", apiCfg.HandlePostLogout) mux.HandleFunc("/attributions", apiCfg.HandleAttributions) mux.HandleFunc("/terms", apiCfg.HandleTerms) diff --git a/sql/queries/users.sql b/sql/queries/users.sql index 832f9c2..b360f99 100644 --- a/sql/queries/users.sql +++ b/sql/queries/users.sql @@ -9,4 +9,10 @@ VALUES ( RETURNING *; -- name: DeleteUsers :exec -DELETE FROM users; \ No newline at end of file +DELETE FROM users; + +-- name: GetUserByLoginCredentials :one +SELECT * +FROM users +WHERE email = $1 +AND password = $2; \ No newline at end of file From 2a91e66b586c4decbdfd63e8523f18c7ea61376e Mon Sep 17 00:00:00 2001 From: ctiller15 Date: Wed, 29 Oct 2025 19:10:37 -0400 Subject: [PATCH 2/6] x - add login html form, fix check on login --- internal/api/handlers.go | 24 ++++++++---------------- internal/api/handlers_test.go | 5 ++--- internal/database/users.sql.go | 12 +++--------- scripts/init_test.sh | 2 +- sql/queries/users.sql | 5 ++--- templates/login.html | 20 ++++++++++++++++++++ 6 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 templates/login.html diff --git a/internal/api/handlers.go b/internal/api/handlers.go index aee42ba..99d4a1b 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -251,8 +251,12 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { "./templates/base.html", )) - // Hash password. - hashedPassword, err := auth.HashPassword(loginDetails.Password) + email := sql.NullString{ + String: loginDetails.Email, + Valid: true, + } + + user, err := a.Db.GetUserByEmail(ctx, email) if err != nil { loginDetails.Valid = false w.WriteHeader(http.StatusUnauthorized) @@ -260,23 +264,11 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { if err != nil { log.Fatal(err) } - return } - // Retrieve user - getUserParams := database.GetUserByLoginCredentialsParams{ - Email: sql.NullString{ - String: loginDetails.Email, - Valid: true, - }, - Password: sql.NullString{ - String: hashedPassword, - Valid: true, - }, - } + valid := auth.CheckPasswordHash(loginDetails.Password, user.Password.String) - user, err := a.Db.GetUserByLoginCredentials(ctx, getUserParams) - if err != nil { + if !valid { loginDetails.Valid = false w.WriteHeader(http.StatusUnauthorized) err = tmpl.Execute(w, loginPageData) diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go index 202f7f0..76f2f74 100644 --- a/internal/api/handlers_test.go +++ b/internal/api/handlers_test.go @@ -196,15 +196,14 @@ func TestHandlePostLogin(t *testing.T) { loginApiCfg := NewAPIConfig(TestEnvVars, DbQueries) loginApiCfg.HandlePostLogin(loginResponse, loginRequest) - loginResult := response.Result() - assert.Equal(t, 302, result.StatusCode) + loginResult := loginResponse.Result() + assert.Equal(t, 307, loginResult.StatusCode) cookies := loginResult.Cookies() assert.NotNil(t, cookies[0]) assert.Equal(t, "token", cookies[0].Name) assert.NotNil(t, cookies[1]) assert.Equal(t, "refresh_token", cookies[1].Name) - t.Errorf("Finish the test!") }) t.Run("Fails to log a user in if invalid username/password", func(t *testing.T) { diff --git a/internal/database/users.sql.go b/internal/database/users.sql.go index 577d843..ed08bd5 100644 --- a/internal/database/users.sql.go +++ b/internal/database/users.sql.go @@ -58,20 +58,14 @@ func (q *Queries) DeleteUsers(ctx context.Context) error { return err } -const getUserByLoginCredentials = `-- name: GetUserByLoginCredentials :one +const getUserByEmail = `-- name: GetUserByEmail :one SELECT id, email, username, firstname, lastname, password, facebook_id, reset_password_token, reset_password_expires, is_premium, premium_level, stripe_customer_id, is_deleted, created_at, updated_at FROM users WHERE email = $1 -AND password = $2 ` -type GetUserByLoginCredentialsParams struct { - Email sql.NullString - Password sql.NullString -} - -func (q *Queries) GetUserByLoginCredentials(ctx context.Context, arg GetUserByLoginCredentialsParams) (User, error) { - row := q.db.QueryRowContext(ctx, getUserByLoginCredentials, arg.Email, arg.Password) +func (q *Queries) GetUserByEmail(ctx context.Context, email sql.NullString) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByEmail, email) var i User err := row.Scan( &i.ID, diff --git a/scripts/init_test.sh b/scripts/init_test.sh index d959b5d..5014883 100755 --- a/scripts/init_test.sh +++ b/scripts/init_test.sh @@ -7,6 +7,6 @@ fi # Name and port defined in docker-compose dbconnstr="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" - +`` cd sql/schema goose postgres $dbconnstr up \ No newline at end of file diff --git a/sql/queries/users.sql b/sql/queries/users.sql index b360f99..4638449 100644 --- a/sql/queries/users.sql +++ b/sql/queries/users.sql @@ -11,8 +11,7 @@ RETURNING *; -- name: DeleteUsers :exec DELETE FROM users; --- name: GetUserByLoginCredentials :one +-- name: GetUserByEmail :one SELECT * FROM users -WHERE email = $1 -AND password = $2; \ No newline at end of file +WHERE email = $1; \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..48c1cc1 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,20 @@ +{{ template "top" .}} + +{{ template "navbar" . }} + +
+
+

Welcome back!

+ +

Log in to your account

+ +
+ + + + +
+
+
+ +{{ template "bottom" }} \ No newline at end of file From 7fcd91916fc37c2fa62406802ce65e1126a4b847 Mon Sep 17 00:00:00 2001 From: ctiller15 Date: Wed, 29 Oct 2025 20:22:05 -0400 Subject: [PATCH 3/6] x - add remaining tests for login failures --- internal/api/handlers.go | 4 ++-- internal/api/handlers_test.go | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 99d4a1b..031aede 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -305,7 +305,7 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { Expires: time.Now().Add(time.Hour * 24), HttpOnly: true, Secure: true, - Domain: "/", + // Domain: "/", SameSite: http.SameSiteStrictMode, }) http.SetCookie(w, &http.Cookie{ @@ -313,7 +313,7 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { Value: refreshTokenString, Expires: time.Now().Add(time.Hour * 30 * 24), HttpOnly: true, - Domain: "/", + // Domain: "/", SameSite: http.SameSiteStrictMode, }) diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go index 76f2f74..033b787 100644 --- a/internal/api/handlers_test.go +++ b/internal/api/handlers_test.go @@ -199,6 +199,8 @@ func TestHandlePostLogin(t *testing.T) { loginResult := loginResponse.Result() assert.Equal(t, 307, loginResult.StatusCode) + assert.Equal(t, loginResult.Header.Get("Location"), "/dashboard") + cookies := loginResult.Cookies() assert.NotNil(t, cookies[0]) assert.Equal(t, "token", cookies[0].Name) @@ -207,9 +209,24 @@ func TestHandlePostLogin(t *testing.T) { }) t.Run("Fails to log a user in if invalid username/password", func(t *testing.T) { - t.Errorf("Finish the test!") + loginFormData := url.Values{ + "email": {"testEmail3@email.com"}, + "password": {"password123"}, + } + + loginRequest, _ := http.NewRequest(http.MethodPost, "/login", strings.NewReader(loginFormData.Encode())) + loginRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + loginResponse := httptest.NewRecorder() + loginApiCfg := NewAPIConfig(TestEnvVars, DbQueries) + loginApiCfg.HandlePostLogin(loginResponse, loginRequest) + + loginResult := loginResponse.Result() + assert.Equal(t, 401, loginResult.StatusCode) + + cookies := loginResult.Cookies() + assert.Len(t, cookies, 0) }) - t.Errorf("Finish the test!") } func TestGetAttributions(t *testing.T) { From 876b33d677208d0b2d842d023ea5a15b00d3dce2 Mon Sep 17 00:00:00 2001 From: ctiller15 Date: Wed, 29 Oct 2025 20:33:13 -0400 Subject: [PATCH 4/6] refactor post login behavior into single function, use within post login method --- internal/api/handlers.go | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 031aede..040890a 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -38,10 +38,6 @@ type LoginForm struct { Valid bool } -type HtmlPageData interface { - SignupPageData | LoginPageData -} - type LoginPageData struct { Title string LoginForm @@ -234,6 +230,21 @@ func expireCookie(w *http.ResponseWriter, cookie_name string) { }) } +func RejectPostLogin( + w http.ResponseWriter, + tmpl *template.Template, + loginDetails *LoginForm, + loginPageData *LoginPageData, + status int) error { + + loginDetails.Valid = false + w.WriteHeader(status) + + err := tmpl.Execute(w, loginPageData) + + return err +} + func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { ctx := r.Context() loginDetails := LoginForm{ @@ -258,30 +269,26 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { user, err := a.Db.GetUserByEmail(ctx, email) if err != nil { - loginDetails.Valid = false - w.WriteHeader(http.StatusUnauthorized) - err = tmpl.Execute(w, loginPageData) + err = RejectPostLogin(w, tmpl, &loginDetails, &loginPageData, http.StatusUnauthorized) if err != nil { log.Fatal(err) } + return } valid := auth.CheckPasswordHash(loginDetails.Password, user.Password.String) if !valid { - loginDetails.Valid = false - w.WriteHeader(http.StatusUnauthorized) - err = tmpl.Execute(w, loginPageData) + err = RejectPostLogin(w, tmpl, &loginDetails, &loginPageData, http.StatusUnauthorized) if err != nil { log.Fatal(err) } + return } tokenString, err := auth.MakeJWT(user.ID, a.Env.Secret) if err != nil { - loginDetails.Valid = false - w.WriteHeader(http.StatusInternalServerError) - err = tmpl.Execute(w, loginPageData) + err = RejectPostLogin(w, tmpl, &loginDetails, &loginPageData, http.StatusInternalServerError) if err != nil { log.Fatal(err) } @@ -290,9 +297,7 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { refreshTokenString, err := auth.MakeRefreshToken() if err != nil { - loginDetails.Valid = false - w.WriteHeader(http.StatusInternalServerError) - err = tmpl.Execute(w, loginPageData) + err = RejectPostLogin(w, tmpl, &loginDetails, &loginPageData, http.StatusInternalServerError) if err != nil { log.Fatal(err) } From 2e4423f67760f71d7388cd0a5f75f78657f61a1e Mon Sep 17 00:00:00 2001 From: ctiller15 Date: Wed, 29 Oct 2025 20:56:11 -0400 Subject: [PATCH 5/6] fix failing tests, switch to 302 over 307 --- internal/api/handlers.go | 98 ++++++++++++----------------------- internal/api/handlers_test.go | 13 +++-- 2 files changed, 41 insertions(+), 70 deletions(-) diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 040890a..e03dd89 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -165,10 +165,11 @@ func (a *APIConfig) HandlePostSignup(w http.ResponseWriter, r *http.Request) { return } - tokenString, err := auth.MakeJWT(user.ID, a.Env.Secret) + err = a.createAndAttachSessionCookies(&w, user) if err != nil { + log.Fatal(err) signupDetails.Valid = false - w.WriteHeader(http.StatusInternalServerError) + w.WriteHeader(http.StatusBadRequest) err = tmpl.Execute(w, signupPageData) if err != nil { log.Fatal(err) @@ -176,58 +177,51 @@ func (a *APIConfig) HandlePostSignup(w http.ResponseWriter, r *http.Request) { return } - refreshTokenString, err := auth.MakeRefreshToken() + http.Redirect(w, r, "/add_new_pet", http.StatusFound) +} + +func expireCookie(w *http.ResponseWriter, cookie_name string) { + http.SetCookie(*w, &http.Cookie{ + Name: cookie_name, + Value: "", + Expires: time.Unix(0, 0), + HttpOnly: true, + }) +} + +func (a *APIConfig) createAndAttachSessionCookies( + w *http.ResponseWriter, + user database.User, +) error { + tokenString, err := auth.MakeJWT(user.ID, a.Env.Secret) if err != nil { - signupDetails.Valid = false - w.WriteHeader(http.StatusInternalServerError) - err = tmpl.Execute(w, signupPageData) - if err != nil { - log.Fatal(err) - } - return + return err } - newPetPageData := BasePageData{ - Title: "Add a new Pet", + refreshTokenString, err := auth.MakeRefreshToken() + if err != nil { + return err } - // Create new template that points to new pet page. - tmpl = template.Must(template.ParseFiles( - "./templates/new_pet.html", - "./templates/base.html", - )) - - http.SetCookie(w, &http.Cookie{ + http.SetCookie(*w, &http.Cookie{ Name: "token", Value: tokenString, Expires: time.Now().Add(time.Hour * 24), HttpOnly: true, Secure: true, - Domain: "/", + // Domain: "/", SameSite: http.SameSiteStrictMode, }) - http.SetCookie(w, &http.Cookie{ + http.SetCookie(*w, &http.Cookie{ Name: "refresh_token", Value: refreshTokenString, Expires: time.Now().Add(time.Hour * 30 * 24), HttpOnly: true, - Domain: "/", + // Domain: "/", SameSite: http.SameSiteStrictMode, }) - w.WriteHeader(http.StatusCreated) - err = tmpl.Execute(w, newPetPageData) - if err != nil { - log.Fatal(err) - } -} -func expireCookie(w *http.ResponseWriter, cookie_name string) { - http.SetCookie(*w, &http.Cookie{ - Name: cookie_name, - Value: "", - Expires: time.Unix(0, 0), - HttpOnly: true, - }) + return nil } func RejectPostLogin( @@ -286,17 +280,9 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { return } - tokenString, err := auth.MakeJWT(user.ID, a.Env.Secret) - if err != nil { - err = RejectPostLogin(w, tmpl, &loginDetails, &loginPageData, http.StatusInternalServerError) - if err != nil { - log.Fatal(err) - } - return - } - - refreshTokenString, err := auth.MakeRefreshToken() + err = a.createAndAttachSessionCookies(&w, user) if err != nil { + log.Fatal(err) err = RejectPostLogin(w, tmpl, &loginDetails, &loginPageData, http.StatusInternalServerError) if err != nil { log.Fatal(err) @@ -304,32 +290,14 @@ func (a *APIConfig) HandlePostLogin(w http.ResponseWriter, r *http.Request) { return } - http.SetCookie(w, &http.Cookie{ - Name: "token", - Value: tokenString, - Expires: time.Now().Add(time.Hour * 24), - HttpOnly: true, - Secure: true, - // Domain: "/", - SameSite: http.SameSiteStrictMode, - }) - http.SetCookie(w, &http.Cookie{ - Name: "refresh_token", - Value: refreshTokenString, - Expires: time.Now().Add(time.Hour * 30 * 24), - HttpOnly: true, - // Domain: "/", - SameSite: http.SameSiteStrictMode, - }) - - http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect) + http.Redirect(w, r, "/dashboard", http.StatusFound) } func (a *APIConfig) HandlePostLogout(w http.ResponseWriter, r *http.Request) { expireCookie(&w, "token") expireCookie(&w, "refresh_token") - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + http.Redirect(w, r, "/", http.StatusFound) } func (a *APIConfig) HandleAttributions(w http.ResponseWriter, r *http.Request) { diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go index 033b787..696f905 100644 --- a/internal/api/handlers_test.go +++ b/internal/api/handlers_test.go @@ -86,7 +86,10 @@ func TestHandlePostSignup(t *testing.T) { apiCfg.HandlePostSignup(response, request) result := response.Result() - assert.Equal(t, 201, result.StatusCode) + assert.Equal(t, 302, result.StatusCode) + + assert.Equal(t, result.Header.Get("Location"), "/add_new_pet") + cookies := result.Cookies() assert.NotNil(t, cookies[0]) assert.Equal(t, "token", cookies[0].Name) @@ -137,7 +140,7 @@ func TestHandleLogout(t *testing.T) { logoutResult := logoutResponse.Result() - assert.Equal(t, 307, logoutResult.StatusCode) + assert.Equal(t, 302, logoutResult.StatusCode) logoutCookies := logoutResult.Cookies() assert.NotNil(t, logoutCookies[0]) assert.Equal(t, "token", logoutCookies[0].Name) @@ -157,7 +160,7 @@ func TestHandleLogout(t *testing.T) { logoutResult := logoutResponse.Result() - assert.Equal(t, 307, logoutResult.StatusCode) + assert.Equal(t, 302, logoutResult.StatusCode) logoutCookies := logoutResult.Cookies() assert.NotNil(t, logoutCookies[0]) assert.Equal(t, "token", logoutCookies[0].Name) @@ -182,7 +185,7 @@ func TestHandlePostLogin(t *testing.T) { apiCfg.HandlePostSignup(response, request) result := response.Result() - assert.Equal(t, 201, result.StatusCode) + assert.Equal(t, 302, result.StatusCode) loginFormData := url.Values{ "email": {"testEmail2@email.com"}, @@ -197,7 +200,7 @@ func TestHandlePostLogin(t *testing.T) { loginApiCfg.HandlePostLogin(loginResponse, loginRequest) loginResult := loginResponse.Result() - assert.Equal(t, 307, loginResult.StatusCode) + assert.Equal(t, 302, loginResult.StatusCode) assert.Equal(t, loginResult.Header.Get("Location"), "/dashboard") From 1cafba416c20770693f665d2a6a34119543f18b4 Mon Sep 17 00:00:00 2001 From: ctiller15 Date: Wed, 29 Oct 2025 20:58:16 -0400 Subject: [PATCH 6/6] cleanup --- scripts/init_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init_test.sh b/scripts/init_test.sh index 5014883..d959b5d 100755 --- a/scripts/init_test.sh +++ b/scripts/init_test.sh @@ -7,6 +7,6 @@ fi # Name and port defined in docker-compose dbconnstr="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" -`` + cd sql/schema goose postgres $dbconnstr up \ No newline at end of file