ali-ahnaf / ali-ahnaf/pocket_pixel

Implement refresh tokens (short-lived access + rotating refresh token)

Đang mở
#41 2 bình luận 0 reaction 0 người được giao Xem trên GitHub
backend up for grabs
Ngôn ngữ chính
TypeScript
Star
14
Fork
91
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

## 🔄 Context / background

Today our authentication uses a **single long-lived JWT** — a 30-day access token issued at login (see [packages/api/src/services/auth.service.ts](packages/api/src/services/auth.service.ts) and the auth routes in [packages/api/src/routes/auth/](packages/api/src/routes/auth/)). That token is what every API request is authenticated with.

A single 30-day token is a security trade-off: if it's stolen, an attacker has access for up to 30 days, and we have no easy way to revoke it.

> 💡 **Terms:**
> - **Access token** — short-lived (e.g. 15 min), sent with every API request to prove who you are.
> - **Refresh token** — longer-lived, stored safely, used *only* to get a new access token when the old one expires. It can be revoked, which logs the user out everywhere.

## 🎯 Problem / goal

Replace the single long-lived token with a **two-token system**:

1. A **short-lived access token** (e.g. 15 minutes) used for normal API calls.
2. A **longer-lived refresh token** (e.g. 30 days) used to silently obtain new access tokens without forcing the user to log in again.

This shrinks the window an attacker has if an access token leaks, and gives us a way to revoke sessions.

## 🛠️ Suggested approach

**Storage / entity**

1. Create a `RefreshToken` entity in [packages/api/src/entities/](packages/api/src/entities/) (extend `BaseEntity`, use soft delete per project conventions). Store a **hashed** token value, the owning `userId`, an `expiresAt`, and a `revokedAt`. Never store the raw token.
2. Generate a migration: `npm run migration:generate`, then `npm run migration:run`.

**Auth service & routes (`packages/api`)**

3. On sign-in / sign-up ([sign-in.route.ts](packages/api/src/routes/auth/sign-in.route.ts), [sign-up.route.ts](packages/api/src/routes/auth/sign-up.route.ts)): issue **both** a short-lived access token and a refresh token; persist the refresh token (hashed).
4. Add a new `POST /api/auth/refresh` route + service method that validates the refresh token, and **rotates** it (issue a new refresh token, revoke the old one) along with a fresh access token.
5. Update sign-out ([sign-out.route.ts](packages/api/src/routes/auth/sign-out.route.ts)) to revoke the user's refresh token(s).
6. Add the access-token expiry as a configurable value (env / `env.ts`).

**Shared DTOs (`packages/shared`)**

7. Add/update request & response contracts for the refresh flow in `packages/shared/src/contracts/` and rebuild with `npm run build:shared`.

**UI (`packages/ui`)**

8. When an API call returns `401` because the access token expired, automatically call `/api/auth/refresh` once and retry the original request (an `axios` response interceptor in [packages/ui/src/lib/api/ApiClient.ts](packages/ui/src/lib/api/ApiClient.ts) is a good place).

> 📝 **Note:** This pairs naturally with moving sessions into `httpOnly` cookies (see the related cookie issue) — ideally the refresh token lives in an `httpOnly` cookie.

## ✅ Acceptance criteria

- [ ] Login/registration returns a short-lived access token **and** a refresh token.
- [ ] Refresh tokens are stored hashed in the database, with expiry and revocation support.
- [ ] A `POST /api/auth/refresh` endpoint issues a new access token and **rotates** the refresh token.
- [ ] Logout revokes the refresh token so it can no longer be used.
- [ ] The UI transparently refreshes an expired access token and retries the failed request.
- [ ] Shared DTOs for the refresh flow live in `packages/shared` and the package is rebuilt.
- [ ] Unit tests cover issuing, refreshing, rotating, and revoking tokens.

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.