fastapi icon indicating copy to clipboard operation
fastapi copied to clipboard

ApiKey Header documentation

Open meandus opened this issue 5 years ago • 42 comments

Hi,

Do you have some documentation or example regarding configuration for ApiKey (header) and how to verify it on FastAPI ?

thanks in advance,

Rémy.

meandus avatar Apr 07 '19 17:04 meandus

Hi @meandus - @tiangolo has a full example that includes that functionality: https://github.com/tiangolo/full-stack-fastapi-postgresql/tree/cd112bd683dcb9017e5f84c0ed3e4974a52e5571/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/app

and plenty of tutorial info here: https://fastapi.tiangolo.com/tutorial/security/intro/

Also - the Gitter channel is already pretty active for these types of questions: https://gitter.im/tiangolo/fastapi

wshayes avatar Apr 07 '19 19:04 wshayes

Thanks @wshayes for your help here! Much appreciated as always :tada:

@meandus if you can use OAuth2, that tutorial and the project generator might help. If somehow you explicitly need something different than OAuth2, with some custom APIKeyHeader (as defined in OpenAPI), yes, it is supported, but it is not properly documented yet.

I suggest you check the security section in the docs shared by William, and after knowing how it works, if you need to explicitly use APIKeyHeader instead of OAuth2, you can from fastapi.security.api_key import APIKeyHeader.

At least while I update the docs with those specifics... :grin:

tiangolo avatar Apr 09 '19 18:04 tiangolo

I found something in the library for apikey and looks good. But for the token (string defined) i dont know what is the best practice for that. Currently i can do a secret.cfg or a json file to store the key and permissions.

I dont know high level of security for this part, thats why im using a apikey in header.

Regards

Le 9 avril 2019 20:42:23 GMT+02:00, "Sebastián Ramírez" [email protected] a écrit :

Thanks @wshayes for your help here! Much appreciated as always :tada:

@meandus if you can use OAuth2, that tutorial and the project generator might help. If somehow you explicitly need something different than OAuth2, with some custom APIKeyHeader (as defined in OpenAPI), yes, it is supported, but it is not properly documented yet.

I suggest you check the security section in the docs shared by William, and after knowing how it works, if you need to explicitly use APIKeyHeader instead of OAuth2, you can from fastapi.security.api_key import APIKeyHeader.

At least while I update the docs with those specifics... :grin:

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/tiangolo/fastapi/issues/142#issuecomment-481380266

-- Envoyé de mon appareil Android avec K-9 Mail. Veuillez excuser ma brièveté.

meandus avatar Apr 09 '19 21:04 meandus

@meandus it depends mostly on your setup, how are you deploying, if you have CI, etc. These kinds of things normally go in environment variables. You can also put them in a file that you read. If the repo is public, then you don't commit that file, just use it during deployment.

If you use the project generators, those settings are read from environment variables, and are passed as environment variables by Docker, reading them from Docker config files.

tiangolo avatar Apr 13 '19 05:04 tiangolo

Hi !

Yes i did env_files. I cant use your docker full stack cause of business constraint but i did a similar stuff based ln your excelent work !

Le 13 avril 2019 07:21:56 GMT+02:00, "Sebastián Ramírez" [email protected] a écrit :

@meandus it depends mostly on your setup, how are you deploying, if you have CI, etc. These kinds of things normally go in environment variables. You can also put them in a file that you read. If the repo is public, then you don't commit that file, just use it during deployment.

If you use the project generators, those settings are read from environment variables, and are passed as environment variables by Docker, reading them from Docker config files.

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/tiangolo/fastapi/issues/142#issuecomment-482777730

-- Envoyé de mon appareil Android avec K-9 Mail. Veuillez excuser ma brièveté.

meandus avatar Apr 13 '19 05:04 meandus

Great! Thanks for reporting back.

tiangolo avatar Apr 16 '19 17:04 tiangolo

It would be really great if someone could at least paste an example of how these headers might be used, i seem to be going through file after file of source code trying to track down anything that I could use as a reference to how to get an API KEY passed via a header to be used as authentication, and i'm not having any luck! Thanks!

bendog avatar Dec 09 '19 09:12 bendog

Hi Ben,

There are several blog articles (see the External Links and Articles section of FastAPI help docs)

e.g.: https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680 https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680

or

https://medium.com/data-rebels/fastapi-how-to-add-basic-and-cookie-authentication-a45c85ef47d3 https://medium.com/data-rebels/fastapi-how-to-add-basic-and-cookie-authentication-a45c85ef47d3

On Dec 9, 2019, at 4:15 AM, Ben Fitzhardinge [email protected] wrote:

It would be really great if someone could at least past an example of how these headers might be used, i seem to be going through file after file of source code trying to track down anything that I could use as a reference to how to get an API KEY passed via a header to be used as authentication.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/tiangolo/fastapi/issues/142?email_source=notifications&email_token=AACZF54UXQHBCWKHF2CMN2TQXYEBTA5CNFSM4HEDSD7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGIMYPQ#issuecomment-563137598, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACZF5YHW5VTRMJEJ6GVQZDQXYEBTANCNFSM4HEDSD7A.

wshayes avatar Dec 09 '19 13:12 wshayes

I ended up working it out, here's how i solved the problem

security.py

from fastapi import Depends, HTTPException
from fastapi.security import APIKeyHeader
from starlette import status


X_API_KEY = APIKeyHeader(name='X-API-Key')


def check_authentication_header(x_api_key: str = Depends(X_API_KEY)):
    """ takes the X-API-Key header and converts it into the matching user object from the database """

    # this is where the SQL query for converting the API key into a user_id will go
    if x_api_key == "1234567890":
        # if passes validation check, return user data for API Key
        # future DB query will go here
        return {
            "id": 1234567890,
            "companies": [1, ],
            "sites": [],
        }
    # else raise 401
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid API Key",
    )

main.py

...
from fastapi import APIRouter, Depends
from models import Result, User
from security import check_authentication_header
...
@app.get("/result/", response_model=List[Result])
def result(user: User = Depends(check_authentication_header)):
    """ return a list of test results """
    print('user', user)
    ...

bendog avatar Dec 09 '19 14:12 bendog

Looks like the beginnings of another great blog article :)

wshayes avatar Dec 09 '19 15:12 wshayes

@tiangolo any ideas on where this documentation should go? I'll try and work on it this week

bendog avatar Feb 24 '20 10:02 bendog

@bendog Thanks for the code snippet! Would be great if you could add it to the documentation with a bit more explanation :-)

I want to add that you obviously need to create a User model first (it confused me that you return a dict but annotate it as a User in main.py).

moreinhardt avatar Feb 26 '20 07:02 moreinhardt

@moreinhardt Sounds good, which file should I use in the repo to create my draft?

bendog avatar Feb 27 '20 05:02 bendog

I don't see any references to this anywhere else in the code. Is this still relevant @tiangolo ? If so I could give it a go :)

gmelodie avatar Jul 03 '20 18:07 gmelodie

@bendog I don't really understand the difference between Security(X_API_KEY) vs Depends(X_API_KEY).

st3fan avatar Aug 07 '20 19:08 st3fan

@st3fan can you link me to the docs about Security(X_API_KEY)? This might have been added since I solved my database driven API key issue

bendog avatar Aug 31 '20 08:08 bendog

if api_key is not necessary in the endpoint you can go for

from fastapi import Security
from fastapi.security.api_key import APIKeyHeader

API_KEY = "1234567asdfgh"
API_KEY_NAME = "X-API-KEY"

api_key_header_auth = APIKeyHeader(name=API_KEY_NAME, auto_error=True)

async def get_api_key(api_key_header: str = Security(api_key_header_auth)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid API Key",
        )

@router.get('/health', dependencies=[Security(get_api_key)])
async def endpoint():

raphaelauv avatar Sep 08 '20 01:09 raphaelauv

Is this available to take up?

mdaj06 avatar Oct 11 '20 20:10 mdaj06

@bendog thanks for the example, but how would I apply this globally across the entire application? I'm playing around with middleware but I'm getting nowhere. This example would require me to add Depends(...) on all routes individually, which will eventually lead to someone messing up and forgetting it, leaving potentially sensitive routes exposed to the entire world

Paradoxis avatar Nov 03 '20 11:11 Paradoxis

You could easily apply it to the whole application?

Here's an example from my own API:

    app.include_router(
        api,
        prefix='/api',
        dependencies=[Security(get_current_user, scopes=['openid'])],
    )

Mause avatar Nov 03 '20 11:11 Mause

Global dependency with APIKeyHeader does not work

api_key_header = APIKeyHeader(name=API_KEY_NAME)

async def get_api_key(api_key_header: str = Security(api_key_header)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )
    return api_key_header


app = FastAPI(dependencies=[Depends(get_api_key)])

https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/

Even if I change it to use Header it still does not work.


async def get_api_key(api_key_header: str = Header(...)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )
    return api_key_header


app = FastAPI(dependencies=[Depends(get_api_key)])

mcat56 avatar Apr 28 '21 14:04 mcat56

Global dependency with APIKeyHeader does not work

api_key_header = APIKeyHeader(name=API_KEY_NAME)

async def get_api_key(api_key_header: str = Security(api_key_header)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )
    return api_key_header


app = FastAPI(dependencies=[Depends(get_api_key)])

https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/

Even if I change it to use Header it still does not work.


async def get_api_key(api_key_header: str = Header(...)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )
    return api_key_header


app = FastAPI(dependencies=[Depends(get_api_key)])

Not sure if this will solve your problem, or whether you figured it out, but for anyone else experiencing this issue: Nginx was stripping any headers with underscores. I changed my API_KEY_NAME="access_token" to "API_KEY_NAME="access-token"" and it fixed my issue.

Alternatively, setting underscores_in_headers on; in your nginx.conf file should also work (I did not test this approach).

AdrianJohnston avatar May 21 '21 06:05 AdrianJohnston

Does anyone know who has the answers to Netcade python essentials part 2 for modules test/quizzes one to four

Hi-tech9 avatar Jun 04 '21 03:06 Hi-tech9

Global dependency with APIKeyHeader does not work

It also wasn't working for me, I had 0.61.0 installed. I upgraded to the latest, 0.67.0, and now it is working for me.

Some choice lines from my application which might help someone piece together a working implementation of APIKeyHeader in 0.67.0 and later:

from fastapi import Depends, FastAPI, HTTPException, Header, Security
from fastapi.security.api_key import APIKeyHeader

API_KEY = "secure"
api_key_header_auth = APIKeyHeader(name="Api-key", auto_error=True)

def get_api_key(api_key_header: str = Security(api_key_header_auth)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid API Key",
        )

app = FastAPI(dependencies=[Depends(get_api_key)])

app.include_router(thing.one, tags=['One'], prefix="/one")
app.include_router(thing.two, tags=['Two'], prefix="/two")

andydavidson avatar Jul 26 '21 15:07 andydavidson

Global dependency with APIKeyHeader does not work

api_key_header = APIKeyHeader(name=API_KEY_NAME)

async def get_api_key(api_key_header: str = Security(api_key_header)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )
    return api_key_header


app = FastAPI(dependencies=[Depends(get_api_key)])

https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/ Even if I change it to use Header it still does not work.


async def get_api_key(api_key_header: str = Header(...)):
    if api_key_header != API_KEY:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )
    return api_key_header


app = FastAPI(dependencies=[Depends(get_api_key)])

Not sure if this will solve your problem, or whether you figured it out, but for anyone else experiencing this issue: Nginx was stripping any headers with underscores. I changed my API_KEY_NAME="access_token" to "API_KEY_NAME="access-token"" and it fixed my issue.

Alternatively, setting underscores_in_headers on; in your nginx.conf file should also work (I did not test this approach).

I was beating myself for the past 3 hours trying figure out why my header keys weren't doing anything. BLESS YOU STRANGER.

AlyanQ avatar Dec 13 '21 10:12 AlyanQ

Is there a secure way to whitelist a certain domain which can use the API without providing an API key? We've struggled with this problem quite a bit, seems surprisingly difficult: https://stackoverflow.com/questions/70579689/whitelist-web-application-for-api-access-without-api-key

aimfeld avatar Jan 05 '22 11:01 aimfeld

I just found out that this even exists after examining how exactly the OAuth2 module works as I wanted to implement exactly this. While I am happy that this functionality is provided natively it is a big bummer that one has to study the code to stumble upon it. This is again a problem of the nonexistent in-depth API reference (#804) :/

septatrix avatar Jan 20 '22 12:01 septatrix

Bump because I don't know what to do:

I've linked PR #4818 that should fix this issue, but it's been stagnant for a month with no review or interaction.

I'm hopeful that people in this thread can indeed find my new docs page via the PR, but I'd like even better to close the current thread with docs on fastAPI's own website!

Any idea how to push this work forward?

OverkillGuy avatar May 30 '22 19:05 OverkillGuy

Hey everyone, can I help in any way.

yashdev9274 avatar Oct 26 '22 07:10 yashdev9274

Also need this. Thanks for the snippets here. What is the difference between Depends and Security?

def check_authentication_header(x_api_key: str = Depends(X_API_KEY)):
def check_authentication_header(x_api_key: str = Security(X_API_KEY)):

app = FastAPI(dependencies=[Depends(check_authentication_header)])
app = FastAPI(dependencies=[Security(check_authentication_header)])

Edit: as per the source code (params.py#L358-L380), Security is a subclass of Depends and only adds the scopes attribute related to OAuth2 scopes. Therefore, no need for this class for the API key security scheme.

Edit: like others, here is my two cents on how to do it with an example.

angely-dev avatar Jan 12 '23 11:01 angely-dev