Remove basic auth and rename methods

All our clients are now using bearer auth so we can remove basic.

Also renamed methods to be more distinct about the differences between
them. Everything goes through bearer auth but in some cases we are
authenticating a user and other times it's either a server or a
particular api key.
This commit is contained in:
Lilia Kai
2024-02-07 07:54:38 -10:00
parent af59779c52
commit 61a5d9a8d7
3 changed files with 48 additions and 59 deletions

View File

@@ -14,7 +14,6 @@ logger.info(f"{CHROME_APP_IDS=}")
BLOCKED_EMAILS = set([e.strip().lower() for e in os.environ.get("BLOCKED_EMAILS", "").split(",")])
logger.info(f"{len(BLOCKED_EMAILS)=}")
basic_security = HTTPBasic()
bearer_security = HTTPBearer()
ALLOW_ANY_EMAIL = "*"
@@ -22,20 +21,45 @@ ALLOW_ANY_EMAIL = "*"
def secure_compare(token, api_key):
return secrets.compare_digest(token.encode("utf8"), api_key.encode("utf8"))
# --------------------- Bearer Auth
# Factory method to create an authentication dependency for a specific key
def api_key_auth(api_key):
async def auth(bearer: HTTPAuthorizationCredentials = Depends(bearer_security), auto_error=True):
assert len(api_key) >= 20, "Invalid API key, must be at least 20 chars"
is_correct = secure_compare(bearer.credentials, api_key)
if is_correct: return True
if auto_error:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Wrong auth credentials",
)
return False
return auth
# --------------------- Static Auth for local AA deployments to add archives to the API
SFP = os.environ.get("STATIC_FILE_PASSWORD", "") # min length is 20 chars
static_api_key_auth = api_key_auth(SFP)
# --------------------- Service Auth for the AA setup tool and Prometheus
SERVICE_PASSWORD = os.environ.get("SERVICE_PASSWORD", "") # min length is 20 chars
service_api_key_auth = api_key_auth(SERVICE_PASSWORD)
# --------------------- Token Auth for AA itself to query the API
API_BEARER_TOKEN = os.environ.get("API_BEARER_TOKEN", "") # min length is 20 chars
async def get_bearer_auth_token_or_jwt(credentials: HTTPAuthorizationCredentials = Depends(bearer_security)):
token_api_key_auth = api_key_auth(API_BEARER_TOKEN)
async def get_token_or_user_auth(credentials: HTTPAuthorizationCredentials = Depends(bearer_security)):
# tries to use the static API_KEY and defaults to google JWT auth
access_token = credentials.credentials
if len(API_BEARER_TOKEN) >= 20:
is_correct_token = secure_compare(access_token, API_BEARER_TOKEN)
if is_correct_token: return ALLOW_ANY_EMAIL
return await get_bearer_auth(credentials)
if token_api_key_auth(access_token, auto_error=False): return ALLOW_ANY_EMAIL
return await get_user_auth(credentials)
async def get_bearer_auth(credentials: HTTPAuthorizationCredentials = Depends(bearer_security)):
async def get_user_auth(credentials: HTTPAuthorizationCredentials = Depends(bearer_security)):
# validates the Bearer token in the case that it requires it
access_token = credentials.credentials
valid_user, info = authenticate_user(access_token)
valid_user, info = authenticate_user(credentials.credentials)
if valid_user: return info
logger.debug(f"TOKEN FAILURE: {valid_user=} {info=}")
raise HTTPException(
@@ -44,7 +68,6 @@ async def get_bearer_auth(credentials: HTTPAuthorizationCredentials = Depends(be
headers={"WWW-Authenticate": "Bearer"},
)
def authenticate_user(access_token):
# https://cloud.google.com/docs/authentication/token-types#access
if type(access_token) != str or len(access_token) < 10: return False, "invalid access_token"
@@ -65,38 +88,3 @@ def authenticate_user(access_token):
logger.warning(f"EXCEPTION occurred: {e}")
return False, f"EXCEPTION occurred"
# Temporary method until all clients migrate from basic to bearer
async def bearer_or_basic_auth(bearer: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error = False)), basic: HTTPBasicCredentials = Depends(HTTPBasic(auto_error = False))):
if bearer: return bearer.credentials
if basic: return basic.password
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
)
# Factory method to create an authentication dependency for a specific key
def api_key_auth(api_key):
async def auth(challenge = Depends(bearer_or_basic_auth)):
assert len(api_key) >= 20, "Invalid API key, must be at least 20 chars"
is_correct = secure_compare(challenge, api_key)
if is_correct: return True
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Wrong auth credentials",
)
return auth
# --------------------- Basic Auth
SFP = os.environ.get("STATIC_FILE_PASSWORD", "") # min length is 20 chars
get_basic_auth = api_key_auth(SFP)
# --------------------- Server-side Auth
SERVICE_PASSWORD = os.environ.get("SERVICE_PASSWORD", "") # min length is 20 chars
get_server_auth = api_key_auth(SERVICE_PASSWORD)