Issue 1:
First check

[*] I added a very descriptive title to this issue.

[*] I used the GitHub search to find a similar issue and didn't find it.

[*] I searched the FastAPI documentation, with the integrated search.

[*] I already searched in Google "How to X in FastAPI" and didn't find any information.

[*] I already read and followed all the tutorial in the docs and didn't find an answer.

[*] I already checked if it is not related to FastAPI but to Pydantic.

[*] I already checked if it is not related to FastAPI but to Swagger UI.

[*] I already checked if it is not related to FastAPI but to ReDoc.

[*] After submitting this, I commit to one of:

Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

Implement a Pull Request for a confirmed bug.

Example

class FactoryDependency:
# go through api_key validation
def call_api_key(self, a= Depends(get_api_key)):
...
# go through jwt validation
def call_jwt(self, b = Depends(auth)):
...

async def __call__(self, request: Request):
headers = request.headers
api_key = case_insensitive_search(headers, "API-KEY")
if api_key:
self.call_api_key()
else:
self.call_jwt()
...

Description

I would like to use a factory dependency for authenticating users to access backend endpoints. Currently, my system supports two ways of authentication: one using api-key and one using jwt. If the user provides either one of them (first check for api-key in the header and if its not, then check for bearer token), the authentication is passed. Would it be possible to have such dependency that performs the check and calls the correct authentication dependency (JWT dependency or Key dependency)? I've gone through Dependencies section of the documentation, but not really sure if calling other dependencies inside a dependency is supported in FastAPI. I tried the above example, but when either of the two methods (call_api_key or call_jwt is called, the dependency doesn't seem to work). Any insights would be greatly appreciated!

Issue 2:
First check

[*] I added a very descriptive title to this issue.

[*] I used the GitHub search to find a similar issue and didn't find it.

[*] I searched the FastAPI documentation, with the integrated search.

[*] I already searched in Google "How to X in FastAPI" and didn't find any information.

[*] I already read and followed all the tutorial in the docs and didn't find an answer.

[*] I already checked if it is not related to FastAPI but to Pydantic.

[*] I already checked if it is not related to FastAPI but to Swagger UI.

[*] I already checked if it is not related to FastAPI but to ReDoc.

[*] After submitting this, I commit to one of:

Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

Implement a Pull Request for a confirmed bug.

Example (with a brief explanation)

I currently have JWT dependency named jwt which makes sure it passes JWT authentication stage before hitting the endpoint like this:

sample_endpoint.py:

from fastapi import APIRouter, Depends, Request
from JWTBearer import JWTBearer
from jwt import jwks

router = APIRouter()

jwt = JWTBearer(jwks)

@router.get("/test_jwt", dependencies=[Depends(jwt)])
async def test_endpoint(request: Request):
return True

Below is the JWT dependency which authenticate users using JWT (source: https://medium.com/datadriveninvestor/jwt-authentication-with-fastapi-and-aws-cognito-1333f7f2729e):

JWTBearer.py

from typing import Dict, Optional, List

from fastapi import HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import jwt, jwk, JWTError
from jose.utils import base64url_decode
from pydantic import BaseModel
from starlette.requests import Request
from starlette.status import HTTP_403_FORBIDDEN

JWK = Dict[str, str]

class JWKS(BaseModel):
keys: List[JWK]

class JWTAuthorizationCredentials(BaseModel):
jwt_token: str
header: Dict[str, str]
claims: Dict[str, str]
signature: str
message: str

class JWTBearer(HTTPBearer):
def __init__(self, jwks: JWKS, auto_error: bool = True):
super().__init__(auto_error=auto_error)

self.kid_to_jwk = {jwk["kid"]: jwk for jwk in jwks.keys}

def verify_jwk_token(self, jwt_credentials: JWTAuthorizationCredentials) -> bool:
try:
public_key = self.kid_to_jwk[jwt_credentials.header["kid"]]
except KeyError:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="JWK public key not found"
)

key = jwk.construct(public_key)
decoded_signature = base64url_decode(jwt_credentials.signature.encode())

return key.verify(jwt_credentials.message.encode(), decoded_signature)

async def __call__(self, request: Request) -> Optional[JWTAuthorizationCredentials]:
credentials: HTTPAuthorizationCredentials = await super().__call__(request)

if credentials:
if not credentials.scheme == "Bearer":
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Wrong authentication method"
)

jwt_token = credentials.credentials

message, signature = jwt_token.rsplit(".", 1)

try:
jwt_credentials = JWTAuthorizationCredentials(
jwt_token=jwt_token,
header=jwt.get_unverified_header(jwt_token),
claims=jwt.get_unverified_claims(jwt_token),
signature=signature,
message=message,
)
except JWTError:
raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="JWK invalid")

if not self.verify_jwk_token(jwt_credentials):
raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="JWK invalid")

return jwt_credentials

jwt.py

import os

import requests
from dotenv import load_dotenv
from fastapi import Depends, HTTPException
from starlette.status import HTTP_403_FORBIDDEN

from app.JWTBearer import JWKS, JWTBearer, JWTAuthorizationCredentials

load_dotenv() # Automatically load environment variables from a '.env' file.

jwks = JWKS.parse_obj(
requests.get(
f"https://cognito-idp.{os.environ.get('COGNITO_REGION')}.amazonaws.com/"
f"{os.environ.get('COGNITO_POOL_ID')}/.well-known/jwks.json"
).json()
)

jwt = JWTBearer(jwks)

async def get_current_user(
credentials: JWTAuthorizationCredentials = Depends(auth)
) -> str:
try:
return credentials.claims["username"]
except KeyError:
HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Username missing")

Description

Depending on the situation, I would like to first check if it has API Key in the header (I will create a separate dependency for this), and if its present, use that to authenticate. Otherwise, I would like to use jwt dependency for authentication. I want to make sure that if either api-key authentication or jwt authentication passes, the user is authenticated. Would this be possible in FastAPI (i.e. having multiple dependencies and if one of them passes, authentication passed). Thank you!

Environment

OS: Ubuntu

FastAPI Version: 0.61.2

Python version: 3.6.9