Skip to content

Commit 00f1085

Browse files
feat: logout token revoke
fix: jwt static key doc: http/authflow.md addition refactor: utils/jwt_utils.go
1 parent 5851e1d commit 00f1085

File tree

2 files changed

+265
-19
lines changed

2 files changed

+265
-19
lines changed

http/authflow.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Auth API Documentation
2+
3+
## Overview
4+
Authentication endpoints for user login, registration, logout, and token refresh.
5+
6+
## Endpoints
7+
8+
### POST /auth/login
9+
Login with email and password.
10+
11+
**Request Body:**
12+
```json
13+
{
14+
"email": "[email protected]",
15+
"password": "password123"
16+
}
17+
```
18+
19+
**Response:**
20+
```json
21+
{
22+
"user": {
23+
"id": "uuid",
24+
"name": "John Doe",
25+
"email": "[email protected]",
26+
"role": "user",
27+
"verified": true,
28+
"created_at": "2024-01-01T00:00:00Z",
29+
"updated_at": "2024-01-01T00:00:00Z"
30+
},
31+
"access_token": "jwt_token",
32+
"refresh_token": "refresh_token",
33+
"expires_at": "2024-01-01T01:00:00Z"
34+
}
35+
```
36+
37+
### POST /auth/register
38+
Register a new user account.
39+
40+
**Request Body:**
41+
```json
42+
{
43+
"name": "John Doe",
44+
"email": "[email protected]",
45+
"password": "password123"
46+
}
47+
```
48+
49+
**Response:** Same as login response (201 Created)
50+
51+
### POST /auth/logout
52+
Logout user and invalidate tokens.
53+
54+
**Headers:**
55+
```
56+
Authorization: Bearer <access_token>
57+
```
58+
59+
**Request Body (optional):**
60+
```json
61+
{
62+
"refresh_token": "refresh_token"
63+
}
64+
```
65+
66+
**Response:**
67+
```json
68+
{
69+
"message": "Logout successful"
70+
}
71+
```
72+
73+
### POST /auth/refresh
74+
Refresh access token using refresh token.
75+
76+
**Request Body:**
77+
```json
78+
{
79+
"refresh_token": "refresh_token"
80+
}
81+
```
82+
83+
**Response:**
84+
```json
85+
{
86+
"access_token": "new_jwt_token",
87+
"refresh_token": "new_refresh_token",
88+
"expires_at": "2024-01-01T01:00:00Z"
89+
}
90+
```

http/swanhtetaungphyo.auth.http

Lines changed: 175 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ Content-Type: {{contentType}}
5252
"password": "password123"
5353
}
5454

55-
### Login with valid credentials
55+
### Login with valid credentials - CAPTURE TOKENS
56+
5657
POST {{baseUrl}}/auth/login
5758
Content-Type: {{contentType}}
5859

@@ -61,6 +62,13 @@ Content-Type: {{contentType}}
6162
"password": "password123"
6263
}
6364

65+
> {%
66+
client.global.set("accessToken", response.body.access_token);
67+
client.global.set("refreshToken", response.body.refresh_token);
68+
client.global.set("userEmail", response.body.user.email);
69+
client.global.set("userId", response.body.user.id);
70+
%}
71+
6472
### Login with invalid credentials (should fail)
6573
POST {{baseUrl}}/auth/login
6674
Content-Type: {{contentType}}
@@ -96,14 +104,19 @@ Content-Type: {{contentType}}
96104
"email": "[email protected]"
97105
}
98106

99-
### Refresh token (replace with actual refresh token from login response)
107+
### Refresh token using captured refresh token
100108
POST {{baseUrl}}/auth/refresh
101109
Content-Type: {{contentType}}
102110

103111
{
104-
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJyZWZyZXNoIiwiaXNzIjoiY21zLXN5cyIsImV4cCI6MTY5NTEyMzQ1NiwiaWF0IjoxNjk0NTE4NjU2LCJuYmYiOjE2OTQ1MTg2NTZ9.example_signature"
112+
"refresh_token": "{{refreshToken}}"
105113
}
106114

115+
> {%
116+
client.global.set("accessToken", response.body.access_token);
117+
client.global.set("refreshToken", response.body.refresh_token);
118+
%}
119+
107120
### Refresh token with invalid token (should fail)
108121
POST {{baseUrl}}/auth/refresh
109122
Content-Type: {{contentType}}
@@ -117,29 +130,36 @@ POST {{baseUrl}}/auth/refresh
117130
Content-Type: {{contentType}}
118131

119132
{
120-
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJpc3MiOiJjbXMtc3lzIiwiZXhwIjoxNjk0NTE5NTU2LCJpYXQiOjE2OTQ1MTg2NTYsIm5iZiI6MTY5NDUxODY1Nn0.example_signature"
133+
"refresh_token": "{{accessToken}}"
121134
}
122135

123136
### Logout with both access and refresh tokens
124137
POST {{baseUrl}}/auth/logout
125138
Content-Type: {{contentType}}
126-
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJpc3MiOiJjbXMtc3lzIiwiZXhwIjoxNjk0NTE5NTU2LCJpYXQiOjE2OTQ1MTg2NTYsIm5iZiI6MTY5NDUxODY1Nn0.example_signature
139+
Authorization: Bearer {{accessToken}}
127140

128141
{
129-
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJyZWZyZXNoIiwiaXNzIjoiY21zLXN5cyIsImV4cCI6MTY5NTEyMzQ1NiwiaWF0IjoxNjk0NTE4NjU2LCJuYmYiOjE2OTQ1MTg2NTZ9.example_signature"
142+
"refresh_token": "{{refreshToken}}"
130143
}
131144

145+
> {%
146+
client.global.clear("accessToken");
147+
client.global.clear("refreshToken");
148+
client.global.clear("userEmail");
149+
client.global.clear("userId");
150+
%}
151+
132152
### Logout with only access token (from Authorization header)
133153
POST {{baseUrl}}/auth/logout
134154
Content-Type: {{contentType}}
135-
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJpc3MiOiJjbXMtc3lzIiwiZXhwIjoxNjk0NTE5NTU2LCJpYXQiOjE2OTQ1MTg2NTYsIm5iZiI6MTY5NDUxODY1Nn0.example_signature
155+
Authorization: Bearer {{accessToken}}
136156

137157
### Logout with only refresh token (in request body)
138158
POST {{baseUrl}}/auth/logout
139159
Content-Type: {{contentType}}
140160

141161
{
142-
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJyZWZyZXNoIiwiaXNzIjoiY21zLXN5cyIsImV4cCI6MTY5NTEyMzQ1NiwiaWF0IjoxNjk0NTE4NjU2LCJuYmYiOjE2OTQ1MTg2NTZ9.example_signature"
162+
"refresh_token": "{{refreshToken}}"
143163
}
144164

145165
### Logout without any tokens (should still succeed)
@@ -151,7 +171,7 @@ POST {{baseUrl}}/auth/refresh
151171
Content-Type: {{contentType}}
152172

153173
{
154-
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2NzgtOTBhYi1jZGVmLTEyMzQtNTY3ODkwYWJjZGVmIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInRva2VuX3R5cGUiOiJyZWZyZXNoIiwiaXNzIjoiY21zLXN5cyIsImV4cCI6MTY5NTEyMzQ1NiwiaWF0IjoxNjk0NTE4NjU2LCJuYmYiOjE2OTQ1MTg2NTZ9.example_signature"
174+
"refresh_token": "{{refreshToken}}"
155175
}
156176

157177
### === WORKFLOW TEST SEQUENCE ===
@@ -163,11 +183,10 @@ Content-Type: {{contentType}}
163183
{
164184
"name": "Test User",
165185
"email": "[email protected]",
166-
"password": "testpass123",
167-
"role": "customer"
186+
"password": "testpass123"
168187
}
169188

170-
### 2. Login with the test user (save the tokens from response)
189+
### 2. Login with the test user - CAPTURE TOKENS
171190
POST {{baseUrl}}/auth/login
172191
Content-Type: {{contentType}}
173192

@@ -176,35 +195,116 @@ Content-Type: {{contentType}}
176195
"password": "testpass123"
177196
}
178197

198+
> {%
199+
client.global.set("testAccessToken", response.body.access_token);
200+
client.global.set("testRefreshToken", response.body.refresh_token);
201+
client.global.set("testUserEmail", response.body.user.email);
202+
client.global.set("testUserId", response.body.user.id);
203+
%}
204+
179205
### 3. Use the access token to access protected endpoint (if you have any)
180206
# GET {{baseUrl}}/protected-endpoint
181-
# Authorization: Bearer {{accessToken}}
207+
# Authorization: Bearer {{testAccessToken}}
182208

183209
### 4. Refresh the token using the refresh token from login
184210
POST {{baseUrl}}/auth/refresh
185211
Content-Type: {{contentType}}
186212

187213
{
188-
"refresh_token": "{{refreshToken}}"
214+
"refresh_token": "{{testRefreshToken}}"
189215
}
190216

191-
### 5. Logout using both tokens (copy actual tokens from previous responses)
217+
> {%
218+
client.global.set("testAccessToken", response.body.access_token);
219+
client.global.set("testRefreshToken", response.body.refresh_token);
220+
%}
221+
222+
### 5. Logout using both tokens
192223
POST {{baseUrl}}/auth/logout
193224
Content-Type: {{contentType}}
194-
Authorization: Bearer {{accessToken}}
225+
Authorization: Bearer {{testAccessToken}}
195226

196227
{
197-
"refresh_token": "{{refreshToken}}"
228+
"refresh_token": "{{testRefreshToken}}"
198229
}
199230

231+
> {%
232+
client.global.clear("testAccessToken");
233+
client.global.clear("testRefreshToken");
234+
client.global.clear("testUserEmail");
235+
client.global.clear("testUserId");
236+
%}
237+
200238
### 6. Try to use the revoked refresh token (should fail)
201239
POST {{baseUrl}}/auth/refresh
202240
Content-Type: {{contentType}}
203241

204242
{
205-
"refresh_token": "{{refreshToken}}"
243+
"refresh_token": "{{testRefreshToken}}"
244+
}
245+
246+
### === AUTHENTICATED REQUESTS EXAMPLES ===
247+
248+
### Get user profile (example protected endpoint)
249+
GET {{baseUrl}}/auth/profile
250+
Authorization: Bearer {{accessToken}}
251+
252+
### Update user profile (example protected endpoint)
253+
PUT {{baseUrl}}/auth/profile
254+
Content-Type: {{contentType}}
255+
Authorization: Bearer {{accessToken}}
256+
257+
{
258+
"name": "Updated Name"
259+
}
260+
261+
### === MULTIPLE USER TESTING ===
262+
263+
### Login as Jane Smith - CAPTURE SEPARATE TOKENS
264+
POST {{baseUrl}}/auth/login
265+
Content-Type: {{contentType}}
266+
267+
{
268+
"email": "[email protected]",
269+
"password": "password123"
270+
}
271+
272+
> {%
273+
client.global.set("janeAccessToken", response.body.access_token);
274+
client.global.set("janeRefreshToken", response.body.refresh_token);
275+
%}
276+
277+
### Use Jane's token for protected endpoint
278+
GET {{baseUrl}}/auth/profile
279+
Authorization: Bearer {{janeAccessToken}}
280+
281+
### Refresh Jane's token
282+
POST {{baseUrl}}/auth/refresh
283+
Content-Type: {{contentType}}
284+
285+
{
286+
"refresh_token": "{{janeRefreshToken}}"
206287
}
207288

289+
> {%
290+
client.global.set("janeAccessToken", response.body.access_token);
291+
client.global.set("janeRefreshToken", response.body.refresh_token);
292+
%}
293+
294+
### Logout Jane
295+
POST {{baseUrl}}/auth/logout
296+
Content-Type: {{contentType}}
297+
Authorization: Bearer {{janeAccessToken}}
298+
299+
{
300+
"refresh_token": "{{janeRefreshToken}}"
301+
}
302+
303+
> {%
304+
client.global.clear("janeAccessToken");
305+
client.global.clear("janeRefreshToken");
306+
%}
307+
208308
### === EDGE CASES ===
209309

210310
### Empty request body tests
@@ -238,4 +338,60 @@ POST {{baseUrl}}/auth/login
238338
{
239339
"email": "[email protected]",
240340
"password": "testpass123"
241-
}
341+
}
342+
343+
### === TOKEN EXPIRATION TESTING ===
344+
345+
### Wait for token to expire and test refresh
346+
POST {{baseUrl}}/auth/refresh
347+
Content-Type: {{contentType}}
348+
349+
{
350+
"refresh_token": "{{refreshToken}}"
351+
}
352+
353+
> {%
354+
client.global.set("accessToken", response.body.access_token);
355+
client.global.set("refreshToken", response.body.refresh_token);
356+
%}
357+
358+
### Test expired access token (if you have short-lived tokens)
359+
GET {{baseUrl}}/auth/profile
360+
Authorization: Bearer {{accessToken}}
361+
362+
### === CONCURRENT LOGIN TESTING ===
363+
364+
### Login from different device (same user)
365+
POST {{baseUrl}}/auth/login
366+
Content-Type: {{contentType}}
367+
368+
{
369+
"email": "[email protected]",
370+
"password": "password123"
371+
}
372+
373+
> {%
374+
client.global.set("device2AccessToken", response.body.access_token);
375+
client.global.set("device2RefreshToken", response.body.refresh_token);
376+
%}
377+
378+
### Test both tokens work
379+
GET {{baseUrl}}/auth/profile
380+
Authorization: Bearer {{accessToken}}
381+
382+
GET {{baseUrl}}/auth/profile
383+
Authorization: Bearer {{device2AccessToken}}
384+
385+
### Logout from device 2
386+
POST {{baseUrl}}/auth/logout
387+
Content-Type: {{contentType}}
388+
Authorization: Bearer {{device2AccessToken}}
389+
390+
{
391+
"refresh_token": "{{device2RefreshToken}}"
392+
}
393+
394+
> {%
395+
client.global.clear("device2AccessToken");
396+
client.global.clear("device2RefreshToken");
397+
%}

0 commit comments

Comments
 (0)