s3rius / FastAPI-template

Feature rich robust FastAPI template.
MIT License
1.83k stars 161 forks source link

Add JWT authentication #116

Open sorasful opened 1 year ago

sorasful commented 1 year ago

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 ?

s3rius commented 1 year ago

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.

sorasful commented 1 year ago

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 commented 1 year ago

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.

s3rius commented 1 year ago

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 commented 1 year ago

But of course, suggestions and Pull requests are welcome.

haffi96 commented 1 year ago

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

s3rius commented 1 year ago

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.

jegork commented 1 year ago

@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.

s3rius commented 1 year ago

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.

andcarnivorous commented 1 year ago

@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

s3rius commented 1 year ago

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

vmjcv commented 10 months ago

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

s3rius commented 10 months ago

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.

andcarnivorous commented 10 months ago

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

vmjcv commented 10 months ago

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 commented 10 months ago

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

谢谢注意到。

Thank you, that's exactly what I need

andcarnivorous commented 10 months ago

@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.

s3rius commented 10 months ago

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

vmjcv commented 9 months ago

@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

s3rius commented 9 months ago

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: ('user@example.com',)]
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.

siwonKH commented 9 months ago

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

Any temporary solutions for this?

madd86 commented 6 months ago

@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.