Compare commits
No commits in common. "5b633498cbaba268ed97703c07f203034a1fd0b4" and "298aca465ecdd97c47bcc391b53e181d9c9cd00a" have entirely different histories.
5b633498cb
...
298aca465e
@ -13,7 +13,7 @@ Endpoints protected with `requireAccessToken` middleware:
|
|||||||
|
|
||||||
- `GET /auth/me`: Get own user's data -> `userResponse` DTO (user ID, username, admin status, and timestamps of creation and last update)
|
- `GET /auth/me`: Get own user's data -> `userResponse` DTO (user ID, username, admin status, and timestamps of creation and last update)
|
||||||
- `POST /auth/logout`: Logout the current user -> Cookie that replaces the refresh token with one that expires immediately
|
- `POST /auth/logout`: Logout the current user -> Cookie that replaces the refresh token with one that expires immediately
|
||||||
- `PUT /auth/owner/`: Update password of the current user with `old_password` and `new_password` -> Cookie with a new refresh token and response with access token and updated user data
|
- `PUT /auth/owner/`: Update password of the current user with `old_password` and `new_password` -> HTTP 204 response
|
||||||
- `DELETE /auth/owner/`: Delete the current user (as the owner) with `password` -> HTTP 204 response and cookie that replaces the refresh token with one that expires immediately
|
- `DELETE /auth/owner/`: Delete the current user (as the owner) with `password` -> HTTP 204 response and cookie that replaces the refresh token with one that expires immediately
|
||||||
- `GET /auth/admin/all`: As an administrator, list all users stored in the system (adjustable with pagination URL parameters) -> Array of `userResponse` DTOs
|
- `GET /auth/admin/all`: As an administrator, list all users stored in the system (adjustable with pagination URL parameters) -> Array of `userResponse` DTOs
|
||||||
- `DELETE /auth/admin/{userID}`: As an administrator, delete a specific user -> HTTP 204 response
|
- `DELETE /auth/admin/{userID}`: As an administrator, delete a specific user -> HTTP 204 response
|
||||||
|
@ -178,11 +178,10 @@ func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]User, e
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePassword = `-- name: UpdatePassword :one
|
const updatePassword = `-- name: UpdatePassword :exec
|
||||||
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 {
|
||||||
@ -190,16 +189,7 @@ type UpdatePasswordParams struct {
|
|||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) (User, error) {
|
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
|
||||||
row := q.db.QueryRow(ctx, updatePassword, arg.ID, arg.PasswordHash)
|
_, err := q.db.Exec(ctx, updatePassword, arg.ID, arg.PasswordHash)
|
||||||
var i User
|
return err
|
||||||
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) (data.User, error)
|
UpdatePassword(ctx context.Context, arg data.UpdatePasswordParams) 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. The new access token and the updated user object's DTO will be returned.
|
// stored in the database.
|
||||||
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,51 +306,19 @@ func (rs authResource) UpdatePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nUSer, err := rs.Users.UpdatePassword(r.Context(), data.UpdatePasswordParams{
|
if 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new pair (access & refresh tokens)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
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,11 +24,10 @@ WHERE id = $1 LIMIT 1;
|
|||||||
SELECT * FROM users
|
SELECT * FROM users
|
||||||
WHERE username = $1 LIMIT 1;
|
WHERE username = $1 LIMIT 1;
|
||||||
|
|
||||||
-- name: UpdatePassword :one
|
-- name: UpdatePassword :exec
|
||||||
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