Skip to content

Commit eef9b87

Browse files
committed
Implement logout function in both backend and frontend to delete HTTP-only cookie on logout
1 parent f98e835 commit eef9b87

File tree

6 files changed

+76
-29
lines changed

6 files changed

+76
-29
lines changed

backend/app/api/deps.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def get_current_user(
3131
session: SessionDep,
3232
http_only_auth_cookie: str = Depends(cookie_scheme),
3333
) -> User:
34-
print("start get_current_user...")
3534
if not http_only_auth_cookie:
3635
raise HTTPException(
3736
status_code=status.HTTP_401_UNAUTHORIZED,
@@ -45,7 +44,6 @@ def get_current_user(
4544
algorithms=[security.ALGORITHM]
4645
)
4746
token_data = TokenPayload(**payload)
48-
print(f"get_current_user token data: {token_data}")
4947
except (InvalidTokenError, ValidationError):
5048
raise HTTPException(
5149
status_code=status.HTTP_403_FORBIDDEN,
@@ -62,7 +60,6 @@ def get_current_user(
6260

6361

6462
CurrentUser = Annotated[User, Depends(get_current_user)]
65-
print(f"CurrentUser {CurrentUser}")
6663

6764
def get_current_active_superuser(current_user: CurrentUser) -> User:
6865
if not current_user.is_superuser:

backend/app/api/routes/login.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from fastapi.responses import HTMLResponse, JSONResponse
66
from fastapi.security import OAuth2PasswordRequestForm
77
from app import crud
8-
from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser
8+
from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser, get_current_user
99
from app.core import security
1010
from app.core.config import settings
1111
from app.core.security import get_password_hash
@@ -22,10 +22,10 @@
2222

2323
@router.post("/login/access-token")
2424
def login_access_token(
25-
session: SessionDep, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
25+
session: SessionDep, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
2626
) -> JSONResponse:
2727
"""
28-
OAuth2 compatible token login, get an access token for future requests
28+
OAuth2 compatible token login, get an access token for future requests (sent in a HTTP-only cookie)
2929
"""
3030
user = crud.authenticate(
3131
session=session, email=form_data.username, password=form_data.password
@@ -36,11 +36,6 @@ def login_access_token(
3636
raise HTTPException(status_code=400, detail="Inactive user")
3737
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
3838
return security.set_response_cookie(user.id, access_token_expires)
39-
# return Token(
40-
# access_token=security.create_access_token(
41-
# user.id, expires_delta=access_token_expires
42-
# ))
43-
4439

4540

4641
@router.post("/login/test-token", response_model=UserPublic)
@@ -122,3 +117,22 @@ def recover_password_html_content(email: str, session: SessionDep) -> Any:
122117
return HTMLResponse(
123118
content=email_data.html_content, headers={"subject:": email_data.subject}
124119
)
120+
121+
122+
@router.post("/logout", dependencies=[Depends(get_current_user)])
123+
def logout() -> JSONResponse:
124+
"""
125+
Delete the HTTP-only cookie during logout
126+
"""
127+
128+
response = JSONResponse(content={"message": "Logout successful"})
129+
130+
response.delete_cookie(
131+
key="http_only_auth_cookie",
132+
path="/",
133+
domain=None,
134+
httponly=True,
135+
samesite="lax",
136+
secure=False, # Should be True in production
137+
)
138+
return response

backend/app/api/routes/users.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ def read_user_me(current_user: CurrentUser) -> Any:
122122
"""
123123
Get current user.
124124
"""
125-
print("read_user_me!!!!!!!")
126125
return current_user
127126

128127

frontend/src/client/sdk.gen.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
ItemsDeleteItemData,
1616
ItemsDeleteItemResponse,
1717
LoginLoginAccessTokenData,
18+
LogoutResponse,
1819
LoginLoginAccessTokenResponse,
1920
LoginTestTokenResponse,
2021
LoginRecoverPasswordData,
@@ -66,6 +67,7 @@ export class ItemsService {
6667
skip: data.skip,
6768
limit: data.limit,
6869
},
70+
withCredentials: true,
6971
errors: {
7072
422: "Validation Error",
7173
},
@@ -88,6 +90,7 @@ export class ItemsService {
8890
url: "/api/v1/items/",
8991
body: data.requestBody,
9092
mediaType: "application/json",
93+
withCredentials: true,
9194
errors: {
9295
422: "Validation Error",
9396
},
@@ -111,6 +114,7 @@ export class ItemsService {
111114
path: {
112115
id: data.id,
113116
},
117+
withCredentials: true,
114118
errors: {
115119
422: "Validation Error",
116120
},
@@ -135,6 +139,7 @@ export class ItemsService {
135139
path: {
136140
id: data.id,
137141
},
142+
withCredentials: true,
138143
body: data.requestBody,
139144
mediaType: "application/json",
140145
errors: {
@@ -160,6 +165,7 @@ export class ItemsService {
160165
path: {
161166
id: data.id,
162167
},
168+
withCredentials: true,
163169
errors: {
164170
422: "Validation Error",
165171
},
@@ -194,6 +200,31 @@ export class LoginService {
194200
})
195201
}
196202

203+
204+
/**
205+
* Login Access Token
206+
* OAuth2 compatible token login, get an access token for future requests
207+
* @param data The data for the request.
208+
* @param data.formData
209+
* @returns Token Successful Response
210+
* @throws ApiError
211+
*/
212+
public static logout(): CancelablePromise<LogoutResponse> {
213+
return __request(OpenAPI, {
214+
method: "POST",
215+
url: "/api/v1/logout",
216+
headers: {
217+
"Content-Type": "application/x-www-form-urlencoded",
218+
},
219+
mediaType: "application/x-www-form-urlencoded",
220+
withCredentials: true,
221+
errors: {
222+
422: "Validation Error",
223+
},
224+
})
225+
}
226+
227+
197228
/**
198229
* Test Token
199230
* Test access token
@@ -331,20 +362,13 @@ export class UsersService {
331362
* @returns UserPublic Successful Response
332363
* @throws ApiError
333364
*/
334-
// public static readUserMe(): CancelablePromise<UsersReadUserMeResponse> {
335-
// console.log("readUserMe")
336-
// let r = __request(OpenAPI, {
337-
// method: "GET",
338-
// url: "/api/v1/users/me",
339-
// withCredentials: true,
340-
// })
341-
// console.log(r.promise)
342-
// return __request(OpenAPI, {
343-
// method: "GET",
344-
// url: "/api/v1/users/me",
345-
// withCredentials: true,
346-
// })
347-
// }
365+
public static readUserMe(): CancelablePromise<UsersReadUserMeResponse> {
366+
return __request(OpenAPI, {
367+
method: "GET",
368+
url: "/api/v1/users/me",
369+
withCredentials: true,
370+
})
371+
}
348372

349373
/**
350374
* Delete User Me
@@ -356,6 +380,7 @@ export class UsersService {
356380
return __request(OpenAPI, {
357381
method: "DELETE",
358382
url: "/api/v1/users/me",
383+
withCredentials: true,
359384
})
360385
}
361386

@@ -375,6 +400,7 @@ export class UsersService {
375400
url: "/api/v1/users/me",
376401
body: data.requestBody,
377402
mediaType: "application/json",
403+
withCredentials: true,
378404
errors: {
379405
422: "Validation Error",
380406
},
@@ -397,6 +423,7 @@ export class UsersService {
397423
url: "/api/v1/users/me/password",
398424
body: data.requestBody,
399425
mediaType: "application/json",
426+
withCredentials: true,
400427
errors: {
401428
422: "Validation Error",
402429
},
@@ -468,6 +495,7 @@ export class UsersService {
468495
},
469496
body: data.requestBody,
470497
mediaType: "application/json",
498+
withCredentials: true,
471499
errors: {
472500
422: "Validation Error",
473501
},
@@ -488,6 +516,7 @@ export class UsersService {
488516
return __request(OpenAPI, {
489517
method: "DELETE",
490518
url: "/api/v1/users/{user_id}",
519+
withCredentials: true,
491520
path: {
492521
user_id: data.userId,
493522
},

frontend/src/client/types.gen.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ export type LoginLoginAccessTokenData = {
141141

142142
export type LoginLoginAccessTokenResponse = HTTPOnlyCookie
143143

144+
export type LogoutResponse = HTTPOnlyCookie
145+
144146
export type LoginTestTokenResponse = UserPublic
145147

146148
export type LoginRecoverPasswordData = {

frontend/src/hooks/useAuth.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,15 @@ const useAuth = () => {
5858
},
5959
})
6060

61-
const logout = () => {
62-
localStorage.removeItem("access_token")
63-
navigate({ to: "/login" })
61+
const logout = async () => {
62+
try {
63+
await LoginService.logout();
64+
localStorage.removeItem("access_token");
65+
navigate({ to: "/login" });
66+
} catch (error) {
67+
console.error("Logout failed:", error);
68+
navigate({ to: "/login" });
69+
}
6470
}
6571

6672
return {

0 commit comments

Comments
 (0)