Skip to content

Commit c7686d9

Browse files
committed
🔒 Enforce OIDC state key
1 parent 9d03d3d commit c7686d9

File tree

4 files changed

+33
-14
lines changed

4 files changed

+33
-14
lines changed

backend/trip/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.8.1"
1+
__version__ = "1.8.2"

backend/trip/routers/auth.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import jwt
2-
from fastapi import APIRouter, Body, HTTPException
2+
from fastapi import APIRouter, Body, Cookie, HTTPException
3+
from fastapi.responses import JSONResponse
34

45
from ..config import settings
56
from ..db.core import init_user_data
@@ -16,21 +17,34 @@
1617
async def auth_params() -> AuthParams:
1718
data = {"oidc": None, "register_enabled": settings.REGISTER_ENABLE}
1819

20+
response = JSONResponse(content=data)
1921
if settings.OIDC_CLIENT_ID and settings.OIDC_CLIENT_SECRET:
2022
oidc_config = await get_oidc_config()
2123
auth_endpoint = oidc_config.get("authorization_endpoint")
22-
data["oidc"] = (
23-
f"{auth_endpoint}?client_id={settings.OIDC_CLIENT_ID}&redirect_uri={settings.OIDC_REDIRECT_URI}&response_type=code&scope=openid+profile"
24+
uri, state = get_oidc_client().create_authorization_url(auth_endpoint)
25+
data["oidc"] = uri
26+
27+
response = JSONResponse(content=data)
28+
response.set_cookie(
29+
"oidc_state", value=state, httponly=True, secure=True, samesite="Lax", max_age=60
2430
)
2531

26-
return data
32+
return response
2733

2834

2935
@router.post("/oidc/login", response_model=Token)
30-
async def oidc_login(session: SessionDep, code: str = Body(..., embed=True)) -> Token:
36+
async def oidc_login(
37+
session: SessionDep,
38+
code: str = Body(..., embed=True),
39+
state: str = Body(..., embed=True),
40+
oidc_state: str = Cookie(None),
41+
) -> Token:
3142
if not (settings.OIDC_CLIENT_ID or settings.OIDC_CLIENT_SECRET):
3243
raise HTTPException(status_code=400, detail="Partial OIDC config")
3344

45+
if not oidc_state or state != oidc_state:
46+
raise HTTPException(status_code=400, detail="OIDC login failed, invalid state")
47+
3448
oidc_config = await get_oidc_config()
3549
token_endpoint = oidc_config.get("token_endpoint")
3650
try:

src/src/app/components/auth/auth.component.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ export class AuthComponent {
4747
private fb: FormBuilder,
4848
) {
4949
this.route.queryParams.subscribe((params) => {
50+
if (!Object.keys(params).length) return;
5051
const code = params["code"];
51-
if (code) {
52-
this.authService.oidcLogin(code).subscribe({
52+
const state = params["state"];
53+
if (code && state) {
54+
this.authService.oidcLogin(code, state).subscribe({
5355
next: (data) => {
5456
if (!data.access_token) {
5557
this.error = "Authentication failed";
@@ -61,9 +63,12 @@ export class AuthComponent {
6163
}
6264
});
6365

64-
this.authService.authParams().subscribe({
65-
next: (params) => (this.authParams = params),
66-
});
66+
// Timeout to handle race condition
67+
setTimeout(() => {
68+
this.authService.authParams().subscribe({
69+
next: (params) => (this.authParams = params),
70+
});
71+
}, 100);
6772

6873
this.redirectURL =
6974
this.route.snapshot.queryParams["redirectURL"] || "/home";
@@ -97,7 +102,7 @@ export class AuthComponent {
97102
authenticate(): void {
98103
this.error = "";
99104
if (this.authParams?.oidc) {
100-
window.location.replace(encodeURI(this.authParams.oidc));
105+
window.location.replace(this.authParams.oidc);
101106
}
102107

103108
this.authService.login(this.authForm.value).subscribe({

src/src/app/services/auth.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ export class AuthService {
109109
);
110110
}
111111

112-
oidcLogin(code: string): Observable<Token> {
112+
oidcLogin(code: string, state: string): Observable<Token> {
113113
return this.httpClient
114-
.post<Token>(this.apiBaseUrl + "/auth/oidc/login", { code })
114+
.post<Token>(this.apiBaseUrl + "/auth/oidc/login", { code, state })
115115
.pipe(
116116
tap((data: any) => {
117117
if (data.access_token && data.refresh_token) {

0 commit comments

Comments
 (0)