Skip to content

Commit 42df852

Browse files
swyatt7lpsinger
andauthored
Adding security scope validation to api endpoints (nasa-gcn#1874)
Fixes nasa-gcn#1862. Addresses nasa-gcn#1833. --------- Co-authored-by: Leo Singer <leo.p.singer@nasa.gov>
1 parent b10fa7a commit 42df852

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

python/across_api/across/api.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
from typing import Annotated, Optional
66

7-
from fastapi import Depends, Query
7+
from fastapi import Depends, Query, Security
88

99
from ..base.api import app
10+
from ..auth.api import scope_authorize
1011
from .hello import Hello
1112
from .resolve import Resolve
1213
from .schema import HelloSchema, ResolveSchema
@@ -47,6 +48,20 @@ def hello(name: YourNameDep) -> HelloSchema:
4748
return Hello(name=name).schema
4849

4950

51+
@app.get(
52+
"/secure_hello",
53+
dependencies=[
54+
Security(scope_authorize, scopes=["gcn.nasa.gov/kafka-public-consumer"])
55+
],
56+
)
57+
async def secure_hello(name: YourNameDep) -> HelloSchema:
58+
"""
59+
This function returns a JSON response with a greeting message and an optional name parameter.
60+
If the name parameter is provided, the greeting message will include the name.
61+
"""
62+
return Hello(name=name).schema
63+
64+
5065
@app.get("/across/resolve")
5166
def resolve(name: SourceNameDep) -> ResolveSchema:
5267
"""

python/across_api/auth/api.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from jose import jwt
1010
from jose.exceptions import JWTError
1111
import httpx # type: ignore
12-
from fastapi import Depends, HTTPException
13-
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
12+
from fastapi import Depends, HTTPException, Security
13+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer, SecurityScopes
1414

1515
from ..base.api import app
1616
from .schema import VerifyAuth
@@ -77,10 +77,26 @@ async def claims(
7777
raise HTTPException(status_code=401, detail=f"Authentication error: {e}")
7878

7979

80-
JWTBearerDep = [Depends(claims)]
80+
async def scope_authorize(
81+
security_scopes: SecurityScopes,
82+
access_token: Annotated[dict, Depends(claims)],
83+
):
84+
# retrieve scopes from access token
85+
scopes = access_token.get("scope", "")
8186

87+
# assuming the jwt scopes will be comma separated
88+
token_scopes = scopes.split(",")
8289

83-
@app.get("/auth/verify", dependencies=JWTBearerDep)
90+
# raise exception if user.role not in endpoint scope
91+
if not all(scope in token_scopes for scope in security_scopes.scopes):
92+
raise HTTPException(
93+
status_code=401,
94+
detail="Bearer token scope(s) not in endpoint scope",
95+
headers={"WWW-Authenticate": "Bearer"},
96+
)
97+
98+
99+
@app.get("/auth/verify", dependencies=[Security(scope_authorize, scopes=[])])
84100
async def verify_authentication() -> VerifyAuth:
85101
"""Verify that the user is authenticated."""
86102
return VerifyAuth()

0 commit comments

Comments
 (0)