feat!: return user dto & create new rt on pw update

This commit is contained in:
ae 2025-04-12 23:48:15 +03:00
parent 298aca465e
commit 0f3fef20e3
Signed by: ae
GPG Key ID: 995EFD5C1B532B3E
3 changed files with 54 additions and 11 deletions

View File

@ -178,10 +178,11 @@ func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]User, e
return items, nil
}
const updatePassword = `-- name: UpdatePassword :exec
const updatePassword = `-- name: UpdatePassword :one
UPDATE users
SET password_hash = $2, updated_at = NOW()
WHERE id = $1
RETURNING id, username, password_hash, is_admin, created_at, updated_at
`
type UpdatePasswordParams struct {
@ -189,7 +190,16 @@ type UpdatePasswordParams struct {
PasswordHash string `json:"password_hash"`
}
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
_, err := q.db.Exec(ctx, updatePassword, arg.ID, arg.PasswordHash)
return err
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) (User, error) {
row := q.db.QueryRow(ctx, updatePassword, arg.ID, arg.PasswordHash)
var i User
err := row.Scan(
&i.ID,
&i.Username,
&i.PasswordHash,
&i.IsAdmin,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@ -71,7 +71,7 @@ type UserStore interface {
ListUsers(ctx context.Context, arg data.ListUsersParams) ([]data.User, error)
GetUserByID(ctx context.Context, id uuid.UUID) (data.User, error)
GetUserByUsername(ctx context.Context, username string) (data.User, error)
UpdatePassword(ctx context.Context, arg data.UpdatePasswordParams) error
UpdatePassword(ctx context.Context, arg data.UpdatePasswordParams) (data.User, error)
DeleteUser(ctx context.Context, id uuid.UUID) error
RevokeAllUserRefreshTokens(ctx context.Context, id uuid.UUID) error
}
@ -271,7 +271,7 @@ func (rs authResource) Get(w http.ResponseWriter, r *http.Request) {
// Handler for updating the current user's password. Performs the same password strength checks as
// the registration handler (`rs.Create`) and revokes any existing refresh tokens the user has
// stored in the database.
// stored in the database. The new access token and the updated user object's DTO will be returned.
func (rs authResource) UpdatePassword(w http.ResponseWriter, r *http.Request) {
type request struct {
OldPassword string `json:"old_password"`
@ -306,19 +306,51 @@ func (rs authResource) UpdatePassword(w http.ResponseWriter, r *http.Request) {
return
}
if err := rs.Users.UpdatePassword(r.Context(), data.UpdatePasswordParams{
nUSer, err := rs.Users.UpdatePassword(r.Context(), data.UpdatePasswordParams{
ID: user.ID,
PasswordHash: string(hashedPassword),
}); err != nil {
})
if err != nil {
respondError(w, http.StatusInternalServerError, "Failed to update password")
return
}
// Revoke all old tokens before generating a new one for this session
if err := rs.Users.RevokeAllUserRefreshTokens(r.Context(), user.ID); err != nil {
log.Error().Msgf("Failed to revoke refresh tokens: %s", err)
}
w.WriteHeader(http.StatusNoContent)
// Generate a new pair (access & refresh tokens)
tokenPair, err := rs.GenerateTokenPair(r.Context(), user.ID, user.IsAdmin)
if err != nil {
respondError(w, http.StatusInternalServerError, "Failed to generate tokens")
return
}
// Set refresh token into a httpOnly cookie
http.SetCookie(w, &http.Cookie{
Name: "notatest.refresh_token",
Value: tokenPair.RefreshToken,
Path: "/api/auth/cookie",
MaxAge: int(refreshTokenDuration.Seconds()),
HttpOnly: true,
Secure: rs.Config.IsProd,
SameSite: http.SameSiteStrictMode,
})
response := map[string]any{
"access_token": tokenPair.AccessToken,
"user": userResponse{
ID: nUSer.ID,
Username: nUSer.Username,
IsAdmin: nUSer.IsAdmin,
CreatedAt: nUSer.CreatedAt,
UpdatedAt: nUSer.UpdatedAt,
},
}
// Return the new access token and the updated user object
respondJSON(w, http.StatusOK, response)
}
// Handler for hard deleting the current user. Requires the user's password as JSON input as a precaution.

View File

@ -24,10 +24,11 @@ WHERE id = $1 LIMIT 1;
SELECT * FROM users
WHERE username = $1 LIMIT 1;
-- name: UpdatePassword :exec
-- name: UpdatePassword :one
UPDATE users
SET password_hash = $2, updated_at = NOW()
WHERE id = $1;
WHERE id = $1
RETURNING *;
-- name: DeleteUser :exec
DELETE FROM users