feat!: return user dto & create new rt on pw update
This commit is contained in:
parent
298aca465e
commit
0f3fef20e3
@ -178,10 +178,11 @@ func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]User, e
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePassword = `-- name: UpdatePassword :exec
|
const updatePassword = `-- name: UpdatePassword :one
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET password_hash = $2, updated_at = NOW()
|
SET password_hash = $2, updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
|
RETURNING id, username, password_hash, is_admin, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdatePasswordParams struct {
|
type UpdatePasswordParams struct {
|
||||||
@ -189,7 +190,16 @@ type UpdatePasswordParams struct {
|
|||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
|
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) (User, error) {
|
||||||
_, err := q.db.Exec(ctx, updatePassword, arg.ID, arg.PasswordHash)
|
row := q.db.QueryRow(ctx, updatePassword, arg.ID, arg.PasswordHash)
|
||||||
return err
|
var i User
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Username,
|
||||||
|
&i.PasswordHash,
|
||||||
|
&i.IsAdmin,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ type UserStore interface {
|
|||||||
ListUsers(ctx context.Context, arg data.ListUsersParams) ([]data.User, error)
|
ListUsers(ctx context.Context, arg data.ListUsersParams) ([]data.User, error)
|
||||||
GetUserByID(ctx context.Context, id uuid.UUID) (data.User, error)
|
GetUserByID(ctx context.Context, id uuid.UUID) (data.User, error)
|
||||||
GetUserByUsername(ctx context.Context, username string) (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
|
DeleteUser(ctx context.Context, id uuid.UUID) error
|
||||||
RevokeAllUserRefreshTokens(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
|
// 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
|
// 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) {
|
func (rs authResource) UpdatePassword(w http.ResponseWriter, r *http.Request) {
|
||||||
type request struct {
|
type request struct {
|
||||||
OldPassword string `json:"old_password"`
|
OldPassword string `json:"old_password"`
|
||||||
@ -306,19 +306,51 @@ func (rs authResource) UpdatePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rs.Users.UpdatePassword(r.Context(), data.UpdatePasswordParams{
|
nUSer, err := rs.Users.UpdatePassword(r.Context(), data.UpdatePasswordParams{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
PasswordHash: string(hashedPassword),
|
PasswordHash: string(hashedPassword),
|
||||||
}); err != nil {
|
})
|
||||||
|
if err != nil {
|
||||||
respondError(w, http.StatusInternalServerError, "Failed to update password")
|
respondError(w, http.StatusInternalServerError, "Failed to update password")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Revoke all old tokens before generating a new one for this session
|
||||||
if err := rs.Users.RevokeAllUserRefreshTokens(r.Context(), user.ID); err != nil {
|
if err := rs.Users.RevokeAllUserRefreshTokens(r.Context(), user.ID); err != nil {
|
||||||
log.Error().Msgf("Failed to revoke refresh tokens: %s", err)
|
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.
|
// Handler for hard deleting the current user. Requires the user's password as JSON input as a precaution.
|
||||||
|
@ -24,10 +24,11 @@ WHERE id = $1 LIMIT 1;
|
|||||||
SELECT * FROM users
|
SELECT * FROM users
|
||||||
WHERE username = $1 LIMIT 1;
|
WHERE username = $1 LIMIT 1;
|
||||||
|
|
||||||
-- name: UpdatePassword :exec
|
-- name: UpdatePassword :one
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET password_hash = $2, updated_at = NOW()
|
SET password_hash = $2, updated_at = NOW()
|
||||||
WHERE id = $1;
|
WHERE id = $1
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
-- name: DeleteUser :exec
|
-- name: DeleteUser :exec
|
||||||
DELETE FROM users
|
DELETE FROM users
|
||||||
|
Loading…
x
Reference in New Issue
Block a user