next router
+ +Next steps¶
+We will now configure the main FastAPI Users object that will expose the API router.
+ + + + + + + + + +diff --git a/404.html b/404.html index 00e1178a..86e067e2 100644 --- a/404.html +++ b/404.html @@ -33,7 +33,7 @@ - + @@ -226,6 +226,222 @@ + + + + + +
We will now configure the main FastAPI Users object that will expose the API router.
+ + + + + + + + + +We will now configure an authentication method.
+ + + + + + + + + +JSON Web Token (JWT) is an internet standard for creating access tokens based on JSON.
+from fastapi_users.authentication import JWTAuthentication + +SECRET = "SECRET" + +auth = JWTAuthentication(secret=SECRET, lifetime_seconds=3600) +
As you can see, instantiation is quite simple. You just have to define a constant SECRET
which is used to encode the token and the lifetime of token (in seconds).
This method expects that you provide a Bearer
authentication with a valid JWT.
curl http://localhost:9000/protected-route -H'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI'
+
We will now configure the main FastAPI Users object that will expose the API router.
+ + + + + + + + + +Coming soon. Track the progress of this feature in ticket #4.
+We will now configure an authentication method.
+ + + + + + + + + +FastAPI Users provides the necessary tools to work with SQL databases thanks to SQLAlchemy Core and encode/databases package for full async support.
+Install the database driver that corresponds to your DBMS:
+pip install databases[postgresql] +
pip install databases[mysql] +
pip install databases[sqlite] +
For the sake of this tutorial from now on, we'll use a simple SQLite databse.
+Let's create a metadata
object and declare our User table.
import databases +import sqlalchemy +from fastapi import FastAPI +from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase +from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base + +DATABASE_URL = "sqlite:///./test.db" + +database = databases.Database(DATABASE_URL) + +Base: DeclarativeMeta = declarative_base() + + +class UserTable(Base, SQLAlchemyBaseUserTable): + pass + + +engine = sqlalchemy.create_engine( + DATABASE_URL, connect_args={"check_same_thread": False} +) + +Base.metadata.create_all(engine) + +users = UserTable.__table__ +user_db = SQLAlchemyUserDatabase(database, users) + +app = FastAPI() + + +@app.on_event("startup") +async def startup(): + await database.connect() + + +@app.on_event("shutdown") +async def shutdown(): + await database.disconnect() +
As you can see, FastAPI Users provides a mixin that will include base fields for our User table. You can of course add you own fields there to fit to your needs!
+We'll now create an SQLAlchemy enigne and ask it to create all the defined tables.
+import databases +import sqlalchemy +from fastapi import FastAPI +from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase +from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base + +DATABASE_URL = "sqlite:///./test.db" + +database = databases.Database(DATABASE_URL) + +Base: DeclarativeMeta = declarative_base() + + +class UserTable(Base, SQLAlchemyBaseUserTable): + pass + + +engine = sqlalchemy.create_engine( + DATABASE_URL, connect_args={"check_same_thread": False} +) + +Base.metadata.create_all(engine) + +users = UserTable.__table__ +user_db = SQLAlchemyUserDatabase(database, users) + +app = FastAPI() + + +@app.on_event("startup") +async def startup(): + await database.connect() + + +@app.on_event("shutdown") +async def shutdown(): + await database.disconnect() +
Tip
+In production, you would probably want to create the tables with Alembic, integrated with migrations, etc.
+The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this.
+import databases +import sqlalchemy +from fastapi import FastAPI +from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase +from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base + +DATABASE_URL = "sqlite:///./test.db" + +database = databases.Database(DATABASE_URL) + +Base: DeclarativeMeta = declarative_base() + + +class UserTable(Base, SQLAlchemyBaseUserTable): + pass + + +engine = sqlalchemy.create_engine( + DATABASE_URL, connect_args={"check_same_thread": False} +) + +Base.metadata.create_all(engine) + +users = UserTable.__table__ +user_db = SQLAlchemyUserDatabase(database, users) + +app = FastAPI() + + +@app.on_event("startup") +async def startup(): + await database.connect() + + +@app.on_event("shutdown") +async def shutdown(): + await database.disconnect() +
Notice that we declare the users
variable, which is the actual SQLAlchemy table behind the table class. We also use our database
instance, which allows us to do asynchronous request to the database.
We will now configure an authentication method.
+The primary objective was to use pure async approach as much as possible. However, we understand that ORM is convenient and useful for many developers. If this feature becomes very demanded, we will add a database adapter for SQLAlchemy ORM.
+ + + + + + + + + +Here is a full working example with JWT authentication to help get you started.
+import databases +import sqlalchemy +from fastapi import FastAPI +from fastapi_users import BaseUser, FastAPIUsers +from fastapi_users.authentication import JWTAuthentication +from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase +from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base + +DATABASE_URL = "sqlite:///./test.db" +SECRET = "SECRET" + + +database = databases.Database(DATABASE_URL) + +Base: DeclarativeMeta = declarative_base() + + +class UserTable(Base, SQLAlchemyBaseUserTable): + pass + + +engine = sqlalchemy.create_engine( + DATABASE_URL, connect_args={"check_same_thread": False} +) + +Base.metadata.create_all(engine) + +users = UserTable.__table__ +user_db = SQLAlchemyUserDatabase(database, users) + + +class User(BaseUser): + pass + + +auth = JWTAuthentication(secret=SECRET, lifetime_seconds=3600) + + +def on_after_forgot_password(user, token): + print(f"User {user.id} has forgot their password. Reset token: {token}") + + +app = FastAPI() +fastapi_users = FastAPIUsers(user_db, auth, User, on_after_forgot_password, SECRET) +app.include_router(fastapi_users.router, prefix="/users", tags=["users"]) + + +@app.on_event("startup") +async def startup(): + await database.connect() + + +@app.on_event("shutdown") +async def shutdown(): + await database.disconnect() +
# Coming soon
+
You're ready to go! Be sure to check the Usage section to understand how yo work with FastAPI Users.
+ + + + + + + + + +FastAPI Users defines a minimal User model for authentication purposes. It is structured like this:
+id
(str
) – Unique identifier of the user. Default to a UUID4.email
(str
) – Email of the user. Validated by email-validator
.is_active
(bool
) – Whether or not the user is active. If not, login and forgot password requests will be denied. Default to True
.is_active
(bool
) – Whether or not the user is a superuser. Useful to implement administration logic. Default to False
.The model is exposed as a Pydantic model mixin.
+from fastapi_users import BaseUser + + +class User(BaseUser): + pass +
You can of course add you own properties there to fit to your needs!
+Depending on your database backend, database configuration will differ a bit.
+ + + + + + + + + + + +We're almost there! The last step is to configure the FastAPIUsers
object that will wire the database adapter, the authentication class and the user model to expose the FastAPI router.
In order to be as unopinionated as possible, you'll have to define your logic after some actions.
+This hook is called after a successful forgot password request. It is called with two arguments: the user which has requested to reset their password and a ready-to-use JWT token that will be accepted by the reset password route.
+Typically, you'll want to send an e-mail with the link (and the token) that allows the user to reset their password.
+You can define it as an async
or standard method.
Example:
+def on_after_forgot_password(user, token): + print(f'User {user.id} has forgot their password. Reset token: {token}') +
FastAPIUsers
¶The last step is to instantiate FastAPIUsers
object with all the elements we defined before. More precisely:
db
: Database adapter instance.auth
: Authentication logic instance.user_model
: Pydantic model of a user.on_after_forgot_password
: Hook called after a forgot password request.reset_password_token_secret
: Secret to encode reset password token.reset_password_token_lifetime_seconds
: Lifetime of reset password token in seconds. Default to one hour.from fastapi_users import FastAPIUsers + +fastapi_users = FastAPIUsers( + user_db, + auth, + User, + on_after_forgot_password, + SECRET, +) +
And then, include the router in the FastAPI app:
+app = FastAPI() +app.include_router(fastapi_users.router, prefix="/users", tags=["users"]) +
Check out a full example that will show you the big picture.
+ + + + + + + + + +
- Ready-to-use and customizable users management for FastAPI + Ready-to-use and customizable users management for FastAPI
Documentation: https://frankie567.github.io/fastapi-users/
Source Code: https://github.com/frankie567/fastapi-users
This library is currently in early stage development. First working version soon!
-Add quickly a registration and authentication system to your FastAPI project. FastAPI Users is designed to be as customizable and adaptable as possible.
+