🔒 Enforce OIDC state key
This commit is contained in:
parent
9d03d3ddb9
commit
c7686d9c84
@ -1 +1 @@
|
|||||||
__version__ = "1.8.1"
|
__version__ = "1.8.2"
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import jwt
|
import jwt
|
||||||
from fastapi import APIRouter, Body, HTTPException
|
from fastapi import APIRouter, Body, Cookie, HTTPException
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from ..config import settings
|
from ..config import settings
|
||||||
from ..db.core import init_user_data
|
from ..db.core import init_user_data
|
||||||
@ -16,21 +17,34 @@ router = APIRouter(prefix="/api/auth", tags=["auth"])
|
|||||||
async def auth_params() -> AuthParams:
|
async def auth_params() -> AuthParams:
|
||||||
data = {"oidc": None, "register_enabled": settings.REGISTER_ENABLE}
|
data = {"oidc": None, "register_enabled": settings.REGISTER_ENABLE}
|
||||||
|
|
||||||
|
response = JSONResponse(content=data)
|
||||||
if settings.OIDC_CLIENT_ID and settings.OIDC_CLIENT_SECRET:
|
if settings.OIDC_CLIENT_ID and settings.OIDC_CLIENT_SECRET:
|
||||||
oidc_config = await get_oidc_config()
|
oidc_config = await get_oidc_config()
|
||||||
auth_endpoint = oidc_config.get("authorization_endpoint")
|
auth_endpoint = oidc_config.get("authorization_endpoint")
|
||||||
data["oidc"] = (
|
uri, state = get_oidc_client().create_authorization_url(auth_endpoint)
|
||||||
f"{auth_endpoint}?client_id={settings.OIDC_CLIENT_ID}&redirect_uri={settings.OIDC_REDIRECT_URI}&response_type=code&scope=openid+profile"
|
data["oidc"] = uri
|
||||||
|
|
||||||
|
response = JSONResponse(content=data)
|
||||||
|
response.set_cookie(
|
||||||
|
"oidc_state", value=state, httponly=True, secure=True, samesite="Lax", max_age=60
|
||||||
)
|
)
|
||||||
|
|
||||||
return data
|
return response
|
||||||
|
|
||||||
|
|
||||||
@router.post("/oidc/login", response_model=Token)
|
@router.post("/oidc/login", response_model=Token)
|
||||||
async def oidc_login(session: SessionDep, code: str = Body(..., embed=True)) -> Token:
|
async def oidc_login(
|
||||||
|
session: SessionDep,
|
||||||
|
code: str = Body(..., embed=True),
|
||||||
|
state: str = Body(..., embed=True),
|
||||||
|
oidc_state: str = Cookie(None),
|
||||||
|
) -> Token:
|
||||||
if not (settings.OIDC_CLIENT_ID or settings.OIDC_CLIENT_SECRET):
|
if not (settings.OIDC_CLIENT_ID or settings.OIDC_CLIENT_SECRET):
|
||||||
raise HTTPException(status_code=400, detail="Partial OIDC config")
|
raise HTTPException(status_code=400, detail="Partial OIDC config")
|
||||||
|
|
||||||
|
if not oidc_state or state != oidc_state:
|
||||||
|
raise HTTPException(status_code=400, detail="OIDC login failed, invalid state")
|
||||||
|
|
||||||
oidc_config = await get_oidc_config()
|
oidc_config = await get_oidc_config()
|
||||||
token_endpoint = oidc_config.get("token_endpoint")
|
token_endpoint = oidc_config.get("token_endpoint")
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -47,9 +47,11 @@ export class AuthComponent {
|
|||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
) {
|
) {
|
||||||
this.route.queryParams.subscribe((params) => {
|
this.route.queryParams.subscribe((params) => {
|
||||||
|
if (!Object.keys(params).length) return;
|
||||||
const code = params["code"];
|
const code = params["code"];
|
||||||
if (code) {
|
const state = params["state"];
|
||||||
this.authService.oidcLogin(code).subscribe({
|
if (code && state) {
|
||||||
|
this.authService.oidcLogin(code, state).subscribe({
|
||||||
next: (data) => {
|
next: (data) => {
|
||||||
if (!data.access_token) {
|
if (!data.access_token) {
|
||||||
this.error = "Authentication failed";
|
this.error = "Authentication failed";
|
||||||
@ -61,9 +63,12 @@ export class AuthComponent {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Timeout to handle race condition
|
||||||
|
setTimeout(() => {
|
||||||
this.authService.authParams().subscribe({
|
this.authService.authParams().subscribe({
|
||||||
next: (params) => (this.authParams = params),
|
next: (params) => (this.authParams = params),
|
||||||
});
|
});
|
||||||
|
}, 100);
|
||||||
|
|
||||||
this.redirectURL =
|
this.redirectURL =
|
||||||
this.route.snapshot.queryParams["redirectURL"] || "/home";
|
this.route.snapshot.queryParams["redirectURL"] || "/home";
|
||||||
@ -97,7 +102,7 @@ export class AuthComponent {
|
|||||||
authenticate(): void {
|
authenticate(): void {
|
||||||
this.error = "";
|
this.error = "";
|
||||||
if (this.authParams?.oidc) {
|
if (this.authParams?.oidc) {
|
||||||
window.location.replace(encodeURI(this.authParams.oidc));
|
window.location.replace(this.authParams.oidc);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.authService.login(this.authForm.value).subscribe({
|
this.authService.login(this.authForm.value).subscribe({
|
||||||
|
|||||||
@ -109,9 +109,9 @@ export class AuthService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
oidcLogin(code: string): Observable<Token> {
|
oidcLogin(code: string, state: string): Observable<Token> {
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.post<Token>(this.apiBaseUrl + "/auth/oidc/login", { code })
|
.post<Token>(this.apiBaseUrl + "/auth/oidc/login", { code, state })
|
||||||
.pipe(
|
.pipe(
|
||||||
tap((data: any) => {
|
tap((data: any) => {
|
||||||
if (data.access_token && data.refresh_token) {
|
if (data.access_token && data.refresh_token) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user