FastAPI-template icon indicating copy to clipboard operation
FastAPI-template copied to clipboard

Add JWT authentication

Open sorasful opened this issue 3 years ago • 22 comments
trafficstars

Hello there !

I imagine that a lot of API's resulting from this repository ends up having an authentication system. I was thinking it may be a good idea to put an option to generate some kind of boilerplate for JWT authentication since, I assume most of people used nowadays.

What do you think about it ?

sorasful avatar Jul 31 '22 15:07 sorasful

Yeah. That's a great point.

I guess the best way to close this issue, is to add JWT authentication implemented by hand. Because FastAPI-JWT isn't maintained now. Or it's a good opportunity to write fastapi-jwt library from scratch.

s3rius avatar Jul 31 '22 15:07 s3rius

Okay, so this implies that we'd need to create a /signup and a /login route. But also that we provide an User model right ? I don't know how you see the JWT mechanism, to me it would be simple : the /login route generates and returns a JWT token that expires after some time, we use it as access token.

I don't know if we want to do all the authorization_code, refresh token, access_token "dance".

And maybe we could add an example of a protected route.

What do you think ?

sorasful avatar Jul 31 '22 19:07 sorasful

I can give it a go and set it up with Ormar which I used recently, but I guess I'll need help for the other ORMs.

sorasful avatar Jul 31 '22 20:07 sorasful

How can JWT be related to ORMs?

Maybe it would be nice to add the JWT service. It may not depend on any ORM. You will add methods to encode and decode tokens. The rest is up to people.

Because one time, I needed JWT authentication for a service without a database.

s3rius avatar Jul 31 '22 20:07 s3rius

But of course, suggestions and Pull requests are welcome.

s3rius avatar Jul 31 '22 20:07 s3rius

Is this currently being worked on or is open to be implemented?

haffi96 avatar Oct 05 '22 14:10 haffi96

Currently it's open to contributions. Personally I would prefer to create a library that can handle different JWT scenarios. After the lib is ready, it's easy to add it in the template.

If you have another solution in mind, feel free to open pull request with your changes.

s3rius avatar Oct 05 '22 14:10 s3rius

@s3rius maybe it is possible to use https://github.com/fastapi-users/fastapi-users ? They provide more or less ready-made solution for auth, supporting JWT and other methods. I think it is very widely used, although they have removed support for ORMs besides SQLAlchemy, but I am currently writing an extension for ormar and tortoise.

jegork avatar Oct 30 '22 18:10 jegork

Hi @jegork and thanks for advise. I don't have much experience with fastapi-users, but It would be really nice if this thing can cover all user-related questions. Also support of different ORMs would be a cool plus.

s3rius avatar Nov 01 '22 14:11 s3rius

@s3rius Hi, I needed something like this as well and I have tried to implement the samples from fastapi-users and it seems to work fine out of the box. However I have never worked with creating cookiecutters themselves, so idk if I may have missed something major or there is something more I should include before opening a PR. https://github.com/s3rius/FastAPI-template/compare/master...andcarnivorous:FastAPI-template:master

andcarnivorous avatar Jan 14 '23 13:01 andcarnivorous

@andcarnivorous Hi. Thanks for your contribution! Just open the pr, and we'll discuss everything within it.

s3rius avatar Jan 14 '23 13:01 s3rius

can you give me the DATABASE template

  1. Run the cli with python3 -m fastapi_template

  2. Give the project name and choose:

    • REST API
    • SQLITE/Postgres database
    • SQLAlchemy as ORM
    • Add fastapi-users support in the section.additional tweaks
  3. test http://127.0.0.1:8000/api/docs#/auth/register_register_api_auth_register_post. it will return Internal Server Error , because

2023-09-25 00:56:54.768 UTC [473] STATEMENT:  SELECT "user".id, "user".email, "user".hashed_password, "user".is_active, "user".is_superuser, "user".is_verified 
FROM "user" 
WHERE lower("user".email) = lower($1::VARCHAR)

2023-09-25 01:01:47.315 UTC [473] ERROR:  relation "user" does not exist at character 121

now, i use the code create table ,But I don't know if there are any missing fields ··· CREATE TABLE "user" (id UUID,email VARCHAR(255),hashed_password VARCHAR(255),is_active BOOLEAN,is_superuser BOOLEAN,is_verified BOOLEAN); ···

maybe i need the user DATABASE template, can you help me?

@andcarnivorous @s3rius

vmjcv avatar Sep 25 '23 01:09 vmjcv

Hi, @vmjcv. The table should follow this protocol: https://github.com/fastapi-users/fastapi-users/blob/ff9fae631cdae00ebc15f051e54728b3c8d11420/fastapi_users/models.py#L6. Anyway, I consider this as a problem, for sure. We need to fix that.

Thanks noticing.

s3rius avatar Sep 25 '23 07:09 s3rius

Hey, are you running the application with docker compose? If not, how are you running the application? @vmjcv

andcarnivorous avatar Sep 25 '23 08:09 andcarnivorous

Hey, are you running the application with docker compose? If not, how are you running the application? @vmjcv

yes, i use it:

docker-compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml --project-directory . build

docker-compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml --project-directory . up --build

The reason for the problem is that the user table does not exist in the database of the template project

vmjcv avatar Sep 25 '23 16:09 vmjcv

嗨,.该表应遵循以下协议:https://github.com/fastapi-users/fastapi-users/blob/ff9fae631cdae00ebc15f051e54728b3c8d11420/fastapi_users/models.py#L6。无论如何,我认为这是一个问题,当然。我们需要解决这个问题。

谢谢注意到。

Thank you, that's exactly what I need

vmjcv avatar Sep 25 '23 16:09 vmjcv

@s3rius is this something that should be fixed somewhere specifically? I have tried to repro but whenever I create a new project and spin up docker compose, I can create users immediately and they are indeed in the postgres table.

andcarnivorous avatar Sep 27 '23 10:09 andcarnivorous

I will take a look today. Maybe there was a problem specific to the setup.

s3rius avatar Sep 27 '23 10:09 s3rius

@s3rius @andcarnivorous hello, Thanks again for all your help. According to this code, I called the create_db_and_tables function to solve the problem of no tables. I didn't setup Alembic because I'm just a newbie and don't know what Alembic is for.

Finally I modified the _setup_db code and register_startup_event code

async def _setup_db(app: FastAPI) -> None:  # pragma: no cover
    """
    Creates connection to the database.

    This function creates SQLAlchemy engine instance,
    session_factory for creating sessions
    and stores them in the application's state property.

    :param app: fastAPI application.
    """
    engine = create_async_engine(str(settings.db_url), echo=settings.db_echo)
    session_factory = async_sessionmaker(
        engine,
        expire_on_commit=False,
    )
    app.state.db_engine = engine
    app.state.db_session_factory = session_factory
    
    ## Modify code
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
def register_startup_event(app: FastAPI) -> Callable[[], Awaitable[None]]:  # pragma: no cover
    """
    Actions to run on application startup.

    This function uses fastAPI app to store data
    in the state, such as db_engine.

    :param app: the fastAPI application.
    :return: function that actually performs actions.
    """

    @app.on_event("startup")
    async def _startup() -> None:  # noqa: WPS430
        app.middleware_stack = None
        ## Modify code
        await _setup_db(app)
        init_redis(app)
        init_rabbit(app)
        setup_prometheus(app)
        app.middleware_stack = app.build_middleware_stack()
        pass  # noqa: WPS420

    return _startup

I don't know if any of this will help you guys, I'm just a newbie

vmjcv avatar Sep 28 '23 21:09 vmjcv

Alembic is a really great thing for creating database migrations. Please consider using it for all new projects that use SQLAlchemy. But that thing should be unrelated to the problem.

Because I also encountered this problem. Even with alembic enabled.

meme-api-1       | sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.ProgrammingError) <class 'asyncpg.exceptions.UndefinedTableError'>: relation "user" does not exist
meme-api-1       | [SQL: SELECT "user".id, "user".email, "user".hashed_password, "user".is_active, "user".is_superuser, "user".is_verified 
meme-api-1       | FROM "user" 
meme-api-1       | WHERE lower("user".email) = lower($1::VARCHAR)]
meme-api-1       | [parameters: ('[email protected]',)]
meme-api-1       | (Background on this error at: https://sqlalche.me/e/20/f405)

We need to fix that. Please keep this issue open. I'll close it once resolved.

s3rius avatar Sep 28 '23 23:09 s3rius

Help I'm facing same error (user table doesn't exist thingy) above.

Any temporary solutions for this?

siwonkh avatar Oct 10 '23 07:10 siwonkh

@s3rius To address the setup issue, I followed these steps, which may assist in resolving it during your setup process:

  1. Initialization of Docker Environment: Executed the standard Docker Compose command:

    docker-compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml --project-directory . up --build
    

    This initializes the Docker environment with the necessary configurations and builds the project.

  2. Database Migration Preparation: Inside the Docker API shell, I executed the Alembic command to autogenerate a new revision:

    alembic revision --autogenerate -m "create user table"
    

    This step creates a new database migration script for the user table.

  3. Dependency Import: In the newly generated migration file, I added an import statement:

    import fastapi_users_db_sqlalchemy
    

    This resolves the fastapi_users_db_sqlalchemy missing error by explicitly importing the required module.

  4. Database Migration Execution: Back in the Docker API shell, I executed the Alembic upgrade:

    alembic upgrade head
    

    This applies the latest migration to the database, effectively creating the user table.

  5. Dependency Addition in pyproject: Modified the pyproject.toml file, specifically under [tool.poetry.dev-dependencies], by adding:

    bcrypt = "4.0.1"
    

    This addition resolves an issue with the bcrypt package.

  6. Docker Restart: Finally, I stopped the Docker environment and re-ran it to ensure all changes were properly applied and integrated.

madd86 avatar Dec 30 '23 01:12 madd86