Rework the documentation

This commit is contained in:
François Voron
2021-09-17 15:14:36 +02:00
parent e31a0a99b5
commit fc59cf11ef
33 changed files with 486 additions and 861 deletions

View File

@@ -9,11 +9,7 @@ from fastapi_users.authentication import CookieAuthentication
SECRET = "SECRET"
auth_backends = []
cookie_authentication = CookieAuthentication(secret=SECRET, lifetime_seconds=3600)
auth_backends.append(cookie_authentication)
```
As you can see, instantiation is quite simple. It accepts the following arguments:
@@ -58,7 +54,3 @@ This method will remove the authentication cookie:
## Authentication
This method expects that you provide a valid cookie in the headers.
## Next steps
We will now configure the main **FastAPI Users** object that will expose the [routers](../routers/index.md).

View File

@@ -9,11 +9,7 @@ from fastapi_users.authentication import JWTAuthentication
SECRET = "SECRET"
auth_backends = []
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/jwt/login")
auth_backends.append(jwt_authentication)
```
As you can see, instantiation is quite simple. It accepts the following arguments:
@@ -69,7 +65,3 @@ from fastapi import Depends, Response
async def refresh_jwt(response: Response, user=Depends(fastapi_users.current_user(active=True))):
return await jwt_authentication.get_login_response(user, response)
```
## Next steps
We will now configure the main **FastAPI Users** object that will expose the [routers](../routers/index.md).

View File

@@ -6,7 +6,7 @@
Let's create a MongoDB connection and instantiate a collection.
```py hl_lines="23 24 25 26"
```py hl_lines="6 7 8 9 10 11"
{!./src/db_mongodb.py!}
```
@@ -17,20 +17,16 @@ You can choose any name for the database and the collection.
## Create the database adapter
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. Create it like this.
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. It should be generated by a FastAPI dependency.
```py hl_lines="34"
```py hl_lines="14 15"
{!./src/db_mongodb.py!}
```
Notice that we pass a reference to your [`UserDB` model](../model.md).
Notice that we pass a reference to your [`UserDB` model](../models.md).
!!! info
The database adapter will automatically create a [unique index](https://docs.mongodb.com/manual/core/index-unique/) on `id` and `email`.
!!! warning
**FastAPI Users** will use its defined [`id` UUID](../model.md) as unique identifier for the user, rather than the builtin MongoDB `_id`.
## Next steps
We will now configure an [authentication method](../authentication/index.md).
**FastAPI Users** will use its defined [`id` UUID](../models.md) as unique identifier for the user, rather than the builtin MongoDB `_id`.

View File

@@ -24,7 +24,7 @@ For the sake of this tutorial from now on, we'll use a simple SQLite databse.
Let's declare our User ORM model.
```py hl_lines="29-33"
```py hl_lines="11-15"
{!./src/db_ormar.py!}
```
@@ -35,19 +35,15 @@ there to fit to your needs!
## Create the database adapter
The database adapter of **FastAPI Users** makes the link between your
database configuration and the users logic. Create it like this.
database configuration and the users logic. It should be generated by a FastAPI dependency.
```py hl_lines="40"
```py hl_lines="22-23"
{!./src/db_ormar.py!}
```
Notice that we pass a reference to your [`UserDB` model](../model.md).
Notice that we pass a reference to your [`UserDB` model](../models.md).
!!! warning
In production, it's strongly recommended to setup a migration system to
update your SQL schemas. See
[Alembic](https://alembic.sqlalchemy.org/en/latest/).
## Next steps
We will now configure an [authentication method](../authentication/index.md).

View File

@@ -22,42 +22,38 @@ For the sake of this tutorial from now on, we'll use a simple SQLite databse.
## Setup User table
Let's create a `metadata` object and declare our User table.
Let's declare our SQLAlchemy `User` table.
```py hl_lines="5 32 33"
```py hl_lines="13 14"
{!./src/db_sqlalchemy.py!}
```
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!
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!
## Create the tables
We'll now create an SQLAlchemy engine and ask it to create all the defined tables.
```py hl_lines="36 37 38 39 40"
```py hl_lines="17 18 19 20"
{!./src/db_sqlalchemy.py!}
```
!!! warning
In production, it's strongly recommended to setup a migration system to update your SQL schemas. See [Alembic](https://alembic.sqlalchemy.org/en/latest/).
## Create the database adapter
## Create the database adapter dependency
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. Create it like this.
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. It should be generated by a FastAPI dependency.
```py hl_lines="42 43"
```py hl_lines="25 26"
{!./src/db_sqlalchemy.py!}
```
Notice that we pass it three things:
* A reference to your [`UserDB` model](../model.md).
* The `users` variable, which is the actual SQLAlchemy table behind the table class.
* A reference to your [`UserDB` model](../models.md).
* A `database` instance, which allows us to do asynchronous request to the database.
## Next steps
We will now configure an [authentication method](../authentication/index.md).
* The `users` variable, which is the actual SQLAlchemy table behind the table class.
## What about SQLAlchemy ORM?

View File

@@ -20,35 +20,33 @@ pip install aiosqlite
For the sake of this tutorial from now on, we'll use a simple SQLite databse.
## Setup User table
## Setup User table and model
Let's declare our User ORM model.
```py hl_lines="8 9"
{!./src/db_tortoise.py!}
```py hl_lines="18 19"
{!./src/db_tortoise_model.py!}
```
As you can see, **FastAPI Users** provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs!
## Tweak `UserDB` model
In order to make the Pydantic model and the Tortoise ORM model working well together, you'll have to add a mixin and some configuration options to your `UserDB` model. Tortoise ORM provides [utilities to ease the integration with Pydantic](https://tortoise-orm.readthedocs.io/en/latest/contrib/pydantic.html) and we'll use them here.
```py hl_lines="5 24 25 26 27"
{!./src/db_tortoise.py!}
```py hl_lines="22 23 24 25 26"
{!./src/db_tortoise_model.py!}
```
The `PydanticModel` mixin adds methods used internally by Tortoise ORM to the Pydantic model so that it can easily transform it back to an ORM model. It expects then that you provide the property `orig_model` which should point to the **User ORM model we defined just above**.
## Create the database adapter
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. Create it like this.
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. It should be generated by a FastAPI dependency.
```py hl_lines="32"
{!./src/db_tortoise.py!}
```py hl_lines="8 9"
{!./src/db_tortoise_adapter.py!}
```
Notice that we pass a reference to your [`UserDB` model](../model.md).
Notice that we pass a reference to your [`UserDB` model](../models.md).
## Register Tortoise
@@ -56,13 +54,16 @@ For using Tortoise ORM we must register our models and database.
Tortoise ORM supports integration with FastAPI out-of-the-box. It will automatically bind startup and shutdown events.
```py hl_lines="35 36 37 38 39 40"
{!./src/db_tortoise.py!}
```py
from tortoise.contrib.fastapi import register_tortoise
register_tortoise(
app,
db_url=DATABASE_URL,
modules={"models": ["models"]},
generate_schemas=True,
)
```
!!! warning
In production, it's strongly recommended to setup a migration system to update your SQL schemas. See [https://tortoise-orm.readthedocs.io/en/latest/migration.html](https://tortoise-orm.readthedocs.io/en/latest/migration.html).
## Next steps
We will now configure an [authentication method](../authentication/index.md).

View File

@@ -8,27 +8,20 @@ Here is a full working example with JWT authentication to help get you started.
## SQLAlchemy
```py
{!./src/full_sqlalchemy.py!}
```
<iframe frameborder="0" width="100%" height="500px" src="https://replit.com/@frankie567/fastapi-users-sqlalchemy?embed=true"></iframe>
## MongoDB
```py
{!./src/full_mongodb.py!}
```
<iframe frameborder="0" width="100%" height="500px" src="https://replit.com/@frankie567/fastapi-users-mongodb?embed=true"></iframe>
## Tortoise ORM
```py
{!./src/full_tortoise.py!}
```
<iframe frameborder="0" width="100%" height="500px" src="https://replit.com/@frankie567/fastapi-users-tortoise?embed=true"></iframe>
## Ormar
```py
{!./src/full_ormar.py!}
```
<iframe frameborder="0" width="100%" height="500px" src="https://replit.com/@frankie567/fastapi-users-ormar?embed=true"></iframe>
## What now?

View File

@@ -1,4 +1,4 @@
# User model
# Models
**FastAPI Users** defines a minimal User model for authentication purposes. It is structured like this:
@@ -67,15 +67,3 @@ class UserUpdate(models.BaseUserUpdate):
class UserDB(User, models.BaseUserDB):
pass
```
## Next steps
Depending on your database backend, the database configuration will differ a bit.
[I'm using SQLAlchemy](databases/sqlalchemy.md)
[I'm using MongoDB](databases/mongodb.md)
[I'm using Tortoise ORM](databases/tortoise.md)
[I'm using ormar](databases/ormar.md)

View File

@@ -46,7 +46,7 @@ class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
class UserUpdate(models.BaseUserUpdate):
pass

View File

@@ -0,0 +1,92 @@
# Overview
The schema below shows you how the library is structured and how each part fit together.
```mermaid
flowchart TB
FASTAPI_USERS{FastAPIUsers}
USER_MANAGER{UserManager}
USER_MANAGER_DEPENDENCY[[get_user_manager]]
CURRENT_USER[[current_user]]
subgraph MODELS[Models]
direction RL
USER[User]
USER_CREATE[UserCreate]
USER_UPDATE[UserUpdate]
USER_DB[UserDB]
end
subgraph DATABASE[Database adapters]
direction RL
SQLALCHEMY[SQLAlchemy]
MONGODB[MongoDB]
TORTOISE[Tortoise ORM]
ORMAR[Ormar]
end
subgraph ROUTERS[Routers]
direction RL
REGISTER[[get_register_router]]
VERIFY[[get_verify_router]]
RESET[[get_reset_password_router]]
AUTH[[get_auth_router]]
OAUTH[[get_oauth_router]]
USERS[[get_users_router]]
end
subgraph AUTH_BACKENDS[Authentication backends]
direction RL
COOKIE[CookieAuthentication]
JWT[JWTAuthentication]
end
DATABASE --> USER_MANAGER
MODELS --> USER_MANAGER
MODELS --> FASTAPI_USERS
USER_MANAGER --> USER_MANAGER_DEPENDENCY
USER_MANAGER_DEPENDENCY --> FASTAPI_USERS
FASTAPI_USERS --> ROUTERS
AUTH_BACKENDS --> AUTH
AUTH_BACKENDS --> FASTAPI_USERS
FASTAPI_USERS --> CURRENT_USER
```
## Models
Pydantic models representing the data structure of a user. Base classes are provided with the required fields to make authentication work. You should sub-class each of them and add your own fields there.
➡️ [Configure the models](./models.md)
## Database adapters
FastAPI Users is compatible with various databases and ORM. To build the interface between those database tools and the library, we provide database adapters classes that you need to instantiate and configure.
➡️ [I'm using SQLAlchemy](databases/sqlalchemy.md)
➡️ [I'm using MongoDB](databases/mongodb.md)
➡️ [I'm using Tortoise ORM](databases/tortoise.md)
➡️ [I'm using ormar](databases/ormar.md)
## Authentication backends
Authentication backends define the way users sessions are managed in your app, like access tokens or cookies.
➡️ [Configure the authentication backends](./authentication/index.md)
## `UserManager`
The `UserManager` object bears most of the logic of FastAPI Users: registration, verification, password reset... We provide a `BaseUserManager` with this common logic; which you should overload to define how to validate passwords or handle events.
This `UserManager` object should be provided through a FastAPI dependency, `get_user_manager`.
➡️ [Configure `UserManager`](./user-manager.md)
## `FastAPIUsers` and routers
Finally, `FastAPIUsers` object is the main class from which you'll be able to generate routers for classic routes like registration or login, but also get the `current_user` dependency factory to inject the authenticated user in your own routes.
➡️ [Configure `FastAPIUsers` and routers](./routers/index.md)

View File

@@ -1,49 +0,0 @@
# Password validation
FastAPI Users **doesn't have any password validation logic by default**. However, there is an argument on the `FastAPIUsers` class so that you can provide your own password validation function.
It'll be applied on each routes that need to validate the input password:
* At registration ([`/register`](../usage/routes.md#post-register))
* At password reset ([`/reset-password`](../usage/routes.md#post-reset-password))
* At profile update ([`/me`](../usage/routes.md#patch-me) and [`/{user_id}`](../usage/routes.md#patch-user_id))
## Configuration
The FastAPIUsers class accepts an optional keyword argument `validate_password`. It expects an async function which accepts in argument:
* `password` (`str`): the password to validate.
* `user` (`Union[UserRegister, User]`): user model which we are currently validating the password. Useful if you want to check that the password doesn't contain the name or the birthdate of the user for example.
This function should return `None` if the password is valid or raise `InvalidPasswordException` if not. This exception expects an argument `reason` telling why the password is invalid. It'll be part of the error response.
## Example
```py
from fastapi_users import FastAPIUsers, InvalidPasswordException
async def validate_password(
password: str,
user: Union[UserCreate, User],
) -> None:
if len(password) < 8:
raise InvalidPasswordException(
reason="Password should be at least 8 characters"
)
if user.email in password:
raise InvalidPasswordException(
reason="Password should not contain e-mail"
)
fastapi_users = FastAPIUsers(
user_db,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
UserDB,
validate_password=validate_password
)
```

View File

@@ -16,7 +16,7 @@ SECRET = "SECRET"
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)
fastapi_users = FastAPIUsers(
user_db,
get_user_manager,
[jwt_authentication],
User,
UserCreate,

View File

@@ -1,12 +1,13 @@
# Routers
We're almost there! The last step is to configure the `FastAPIUsers` object that will wire the database adapter, the authentication classes and let us generate the actual **API routes**.
We're almost there! The last step is to configure the `FastAPIUsers` object that will wire the user manager, the authentication classes and let us generate the actual **API routes**.
## Configure `FastAPIUsers`
Configure `FastAPIUsers` object with all the elements we defined before. More precisely:
* `db`: Database adapter instance.
* `get_user_manager`: Dependency callable getter to inject the
user manager class instance. See [UserManager](../user-manager.md).
* `auth_backends`: List of authentication backends. See [Authentication](../authentication/index.md).
* `user_model`: Pydantic model of a user.
* `user_create_model`: Pydantic model for creating a user.
@@ -17,8 +18,8 @@ Configure `FastAPIUsers` object with all the elements we defined before. More pr
from fastapi_users import FastAPIUsers
fastapi_users = FastAPIUsers(
user_db,
auth_backends,
get_user_manager,
[jwt_authentication],
User,
UserCreate,
UserUpdate,

View File

@@ -16,7 +16,7 @@ SECRET = "SECRET"
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)
fastapi_users = FastAPIUsers(
user_db,
get_user_manager,
[jwt_authentication],
User,
UserCreate,
@@ -31,24 +31,3 @@ app.include_router(
tags=["auth"],
)
```
## After register
You can provide a custom function to be called after a successful registration. It is called with **two arguments**: the **user** that has just registered, and the original **`Request` object**.
Typically, you'll want to **send a welcome e-mail** or add it to your marketing analytics pipeline.
You can define it as an `async` or standard method.
Example:
```py
def on_after_register(user: UserDB, request: Request):
print(f"User {user.id} has registered.")
app.include_router(
fastapi_users.get_register_router(on_after_register),
prefix="/auth",
tags=["auth"],
)
```

View File

@@ -10,9 +10,13 @@ Check the [routes usage](../../usage/routes.md) to learn how to use them.
from fastapi import FastAPI
from fastapi_users import FastAPIUsers
SECRET = "SECRET"
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)
fastapi_users = FastAPIUsers(
user_db,
auth_backends,
get_user_manager,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
@@ -21,62 +25,7 @@ fastapi_users = FastAPIUsers(
app = FastAPI()
app.include_router(
fastapi_users.get_reset_password_router("SECRET"),
prefix="/auth",
tags=["auth"],
)
```
Parameters:
* `reset_password_token_secret` (`Union[str, pydantic.SecretStr]`): Secret to encode reset password token.
* `reset_password_token_lifetime_seconds` (`int`): Lifetime of reset password token. **Defaults to 3600**.
* `after_forgot_password`: Optional function called after a successful forgot password request. See below.
## After forgot password
You can provide a custom function to be called after a successful forgot password request. It is called with **three arguments**:
* The **user** which has requested to reset their password.
* A ready-to-use **JWT token** that will be accepted by the reset password route.
* The original **`Request` object**.
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:
```py
def on_after_forgot_password(user: UserDB, token: str, request: Request):
print(f"User {user.id} has forgot their password. Reset token: {token}")
app.include_router(
fastapi_users.get_reset_password_router("SECRET", after_forgot_password=on_after_forgot_password),
prefix="/auth",
tags=["auth"],
)
```
## After reset password
You can provide a custom function to be called after a successful password reset. It is called with **two arguments**:
* The **user** which has reset their password.
* The original **`Request` object**.
For example, you may want to **send an e-mail** to the concerned user to warn him that their password has been changed and that they should take action if they think they have been hacked.
You can define it as an `async` or standard method.
Example:
```py
def on_after_reset_password(user: UserDB, request: Request):
print(f"User {user.id} has reset their password.")
app.include_router(
fastapi_users.get_reset_password_router("SECRET", after_reset_password=on_after_reset_password),
fastapi_users.get_reset_password_router(),
prefix="/auth",
tags=["auth"],
)

View File

@@ -8,9 +8,13 @@ This router provides routes to manage users. Check the [routes usage](../../usag
from fastapi import FastAPI
from fastapi_users import FastAPIUsers
SECRET = "SECRET"
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)
fastapi_users = FastAPIUsers(
user_db,
auth_backends,
get_user_manager,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
@@ -36,28 +40,3 @@ app.include_router(
tags=["users"],
)
```
## After update
You can provide a custom function to be called after a successful update user request. It is called with **three arguments**:
* The **user** which was updated.
* The dictionary containing the updated fields.
* The original **`Request` object**.
It may be useful, for example, if you wish to update your user in a data analytics or customer success platform.
You can define it as an `async` or standard method.
Example:
```py
def on_after_update(user: UserDB, updated_user_data: Dict[str, Any], request: Request):
print(f"User {user.id} has been updated with the following data: {updated_user_data}")
app.include_router(
fastapi_users.get_users_router(on_after_update),
prefix="/users",
tags=["users"],
)
```

View File

@@ -11,9 +11,13 @@ This router provides routes to manage user email verification. Check the [routes
from fastapi import FastAPI
from fastapi_users import FastAPIUsers
SECRET = "SECRET"
jwt_authentication = JWTAuthentication(secret=SECRET, lifetime_seconds=3600)
fastapi_users = FastAPIUsers(
user_db,
auth_backends,
get_user_manager,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
@@ -22,63 +26,7 @@ fastapi_users = FastAPIUsers(
app = FastAPI()
app.include_router(
fastapi_users.get_verify_router("SECRET"),
prefix="/auth",
tags=["auth"],
)
```
Parameters:
* `verification_token_secret` (`Union[str, pydantic.SecretStr]`): Secret to encode verify token.
* `verification_token_lifetime_seconds` (`int`): Lifetime of verify token. **Defaults to 3600**.
* `after_verification_request`: Optional function called after a successful verify request. See below.
* `after_verification`: Optional function called after a successful verification. See below.
## After verification request
You can provide a custom function to be called after a successful verification request. It is called with **three arguments**:
* The **user** for which the verification has been requested.
* A ready-to-use **JWT token** that will be accepted by the verify route.
* The original **`Request` object**.
Typically, you'll want to **send an e-mail** with the link (and the token) that allows the user to verify their e-mail.
You can define it as an `async` or standard method.
Example:
```py
def after_verification_request(user: UserDB, token: str, request: Request):
print(f"Verification requested for user {user.id}. Verification token: {token}")
app.include_router(
fastapi_users.get_verify_router("SECRET", after_verification_request=after_verification_request),
prefix="/auth",
tags=["auth"],
)
```
## After verification
You can provide a custom function to be called after a successful user verification. It is called with **two arguments**:
* The **user** that has been verified.
* The original **`Request` object**.
This may be useful if you wish to send another e-mail or store this information in a data analytics or customer success platform.
You can define it as an `async` or standard method.
Example:
```py
def after_verification(user: UserDB, request: Request):
print(f"{user.id} is now verified.")
app.include_router(
fastapi_users.get_verify_router("SECRET", after_verification=after_verification),
fastapi_users.get_verify_router(),
prefix="/auth",
tags=["auth"],
)

View File

@@ -0,0 +1,227 @@
# UserManager
The `UserManager` class is the core logic of FastAPI Users. We provide the `BaseUserManager` class which you should extend to set some parameters and define logic, for example when a user just registered or forgot its password.
It's designed to be easily extensible and customizable so that you can integrate less generic logic.
## Create your `UserManager` class
You should define your own version of the `UserManager` class to set various parameters.
```py hl_lines="13-29"
{!./src/user_manager.py!}
```
As you can see, you have to define here various attributes and methods. You can find the complete list of those below.
## Create `get_user_manager` dependency
The `UserManager` class will be injected at runtime using a FastAPI dependency. This way, you can run it in a database session or swap it with a mock during testing.
```py hl_lines="31-32"
{!./src/user_manager.py!}
```
Notice that we use the `get_user_db` dependency we defined earlier to inject the database instance.
## Customize attributes and methods
### Attributes
* `user_db_model`: Pydantic model of a DB representation of a user.
* `reset_password_token_secret`: Secret to encode reset password token. **Use a strong passphrase and keep it secure.**
* `reset_password_token_lifetime_seconds`: Lifetime of reset password token. Defaults to 3600.
* `reset_password_token_audience`: JWT audience of reset password token. Defaults to `fastapi-users:reset`.
* `verification_token_secret`: Secret to encode verification token. **Use a strong passphrase and keep it secure.**
* `verification_token_lifetime_seconds`: Lifetime of verification token. Defaults to 3600.
* `verification_token_audience`: JWT audience of verification token. Defaults to `fastapi-users:verify`.
### Methods
#### `validate_password`
Validate a password.
**Arguments**
* `password` (`str`): the password to validate.
* `user` (`Union[UserCreate, User]`): user model which we are currently validating the password. Useful if you want to check that the password doesn't contain the name or the birthdate of the user for example.
**Output**
This function should return `None` if the password is valid or raise `InvalidPasswordException` if not. This exception expects an argument `reason` telling why the password is invalid. It'll be part of the error response.
**Example**
```py
from fastapi_users import BaseUserManager, InvalidPasswordException
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def validate_password(
self,
password: str,
user: Union[UserCreate, UserDB],
) -> None:
if len(password) < 8:
raise InvalidPasswordException(
reason="Password should be at least 8 characters"
)
if user.email in password:
raise InvalidPasswordException(
reason="Password should not contain e-mail"
)
```
#### `on_after_register`
Perform logic after successful user registration.
Typically, you'll want to **send a welcome e-mail** or add it to your marketing analytics pipeline.
**Arguments**
* `user` (`UserDB`): the registered user.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def on_after_register(self, user: UserDB, request: Optional[Request] = None):
print(f"User {user.id} has registered.")
```
#### `on_after_update`
Perform logic after successful user update.
It may be useful, for example, if you wish to update your user in a data analytics or customer success platform.
**Arguments**
* `user` (`UserDB`): the updated user.
* `update_dict` (`Dict[str, Any]`): dictionary with the updated user fields.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def on_after_update(
self,
user: UserDB,
update_dict: Dict[str, Any],
request: Optional[Request] = None,
):
print(f"User {user.id} has been updated with {update_dict}.")
```
#### `on_after_request_verify`
Perform logic after successful verification request.
Typically, you'll want to **send an e-mail** with the link (and the token) that allows the user to verify their e-mail.
**Arguments**
* `user` (`UserDB`): the user to verify.
* `token` (`str`): the verification token.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def on_after_request_verify(
self, user: UserDB, token: str, request: Optional[Request] = None
):
print(f"Verification requested for user {user.id}. Verification token: {token}")
```
#### `on_after_verify`
Perform logic after successful user verification.
This may be useful if you wish to send another e-mail or store this information in a data analytics or customer success platform.
**Arguments**
* `user` (`UserDB`): the verified user.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def on_after_request_verify(
self, user: UserDB, request: Optional[Request] = None
):
print(f"User {user.id} has been verified")
```
#### `on_after_forgot_password`
Perform logic after successful forgot password request.
Typically, you'll want to **send an e-mail** with the link (and the token) that allows the user to reset their password.
**Arguments**
* `user` (`UserDB`): the user that forgot its password.
* `token` (`str`): the forgot password token
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def on_after_request_verify(
self, user: UserDB, token: str, request: Optional[Request] = None
):
print(f"User {user.id} has forgot their password. Reset token: {token}")
```
#### `on_after_reset_password`
Perform logic after successful password reset.
For example, you may want to **send an e-mail** to the concerned user to warn him that their password has been changed and that they should take action if they think they have been hacked.
**Arguments**
* `user` (`UserDB`): the user that reset its password.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager
class UserManager(BaseUserManager[UserCreate, UserDB]):
# ...
async def on_after_reset_password(self, user: UserDB, request: Optional[Request] = None):
print(f"User {user.id} has reset their password.")
```

View File

@@ -28,4 +28,4 @@ pip install 'fastapi-users[ormar]'
---
That's it! Now, let's have a look at our [User model](./configuration/model.md).
That's it! Now, let's have a look at our [User model](./configuration/models.md).

View File

@@ -1,24 +1,7 @@
import motor.motor_asyncio
from fastapi import FastAPI
from fastapi_users import models
from fastapi_users.db import MongoDBUserDatabase
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB):
pass
from .models import UserDB
DATABASE_URL = "mongodb://localhost:27017"
client = motor.motor_asyncio.AsyncIOMotorClient(
@@ -28,7 +11,5 @@ db = client["database_name"]
collection = db["users"]
app = FastAPI()
user_db = MongoDBUserDatabase(UserDB, collection)
def get_user_db():
yield MongoDBUserDatabase(UserDB, collection)

View File

@@ -1,26 +1,8 @@
import databases
import sqlalchemy
from fastapi import FastAPI
from fastapi_users import models
from fastapi_users.db import OrmarBaseUserModel, OrmarUserDatabase
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB):
pass
DATABASE_URL = "sqlite:///test.db"
metadata = sqlalchemy.MetaData()
database = databases.Database(DATABASE_URL)
@@ -37,20 +19,5 @@ engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
user_db = OrmarUserDatabase(UserDB, UserModel)
app = FastAPI()
app.state.database = database
@app.on_event("startup")
async def startup() -> None:
database_ = app.state.database
if not database_.is_connected:
await database_.connect()
@app.on_event("shutdown")
async def shutdown() -> None:
database_ = app.state.database
if database_.is_connected:
await database_.disconnect()
def get_user_db():
yield OrmarUserDatabase(UserDB, UserModel)

View File

@@ -1,31 +1,12 @@
import databases
import sqlalchemy
from fastapi import FastAPI
from fastapi_users import models
from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB):
pass
from .models import UserDB
DATABASE_URL = "sqlite:///./test.db"
database = databases.Database(DATABASE_URL)
Base: DeclarativeMeta = declarative_base()
@@ -36,20 +17,10 @@ class UserTable(Base, SQLAlchemyBaseUserTable):
engine = sqlalchemy.create_engine(
DATABASE_URL, connect_args={"check_same_thread": False}
)
Base.metadata.create_all(engine)
users = UserTable.__table__
user_db = SQLAlchemyUserDatabase(UserDB, database, users)
app = FastAPI()
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
def get_user_db():
yield SQLAlchemyUserDatabase(UserDB, database, users)

View File

@@ -1,40 +0,0 @@
from fastapi import FastAPI
from fastapi_users import models
from fastapi_users.db import TortoiseBaseUserModel, TortoiseUserDatabase
from tortoise.contrib.fastapi import register_tortoise
from tortoise.contrib.pydantic import PydanticModel
class UserModel(TortoiseBaseUserModel):
pass
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB, PydanticModel):
class Config:
orm_mode = True
orig_model = UserModel
DATABASE_URL = "sqlite://./test.db"
user_db = TortoiseUserDatabase(UserDB, UserModel)
app = FastAPI()
register_tortoise(
app,
db_url=DATABASE_URL,
modules={"models": ["path_to_your_package"]},
generate_schemas=True,
)

View File

@@ -0,0 +1,9 @@
from fastapi_users.db import TortoiseUserDatabase
from .models import UserDB, UserModel
DATABASE_URL = "sqlite://./test.db"
def get_user_db():
yield TortoiseUserDatabase(UserDB, UserModel)

View File

@@ -0,0 +1,25 @@
from fastapi_users import models
from fastapi_users.db import TortoiseBaseUserModel
from tortoise.contrib.pydantic import PydanticModel
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(models.BaseUserUpdate):
pass
class UserModel(TortoiseBaseUserModel):
pass
class UserDB(User, models.BaseUserDB, PydanticModel):
class Config:
orm_mode = True
orig_model = UserModel

View File

@@ -1,80 +0,0 @@
import motor.motor_asyncio
from fastapi import FastAPI, Request
from fastapi_users import FastAPIUsers, models
from fastapi_users.authentication import JWTAuthentication
from fastapi_users.db import MongoDBUserDatabase
DATABASE_URL = "mongodb://localhost:27017"
SECRET = "SECRET"
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB):
pass
client = motor.motor_asyncio.AsyncIOMotorClient(
DATABASE_URL, uuidRepresentation="standard"
)
db = client["database_name"]
collection = db["users"]
user_db = MongoDBUserDatabase(UserDB, collection)
def on_after_register(user: UserDB, request: Request):
print(f"User {user.id} has registered.")
def on_after_forgot_password(user: UserDB, token: str, request: Request):
print(f"User {user.id} has forgot their password. Reset token: {token}")
def after_verification_request(user: UserDB, token: str, request: Request):
print(f"Verification requested for user {user.id}. Verification token: {token}")
jwt_authentication = JWTAuthentication(
secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/jwt/login"
)
app = FastAPI()
fastapi_users = FastAPIUsers(
user_db,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
UserDB,
)
app.include_router(
fastapi_users.get_auth_router(jwt_authentication), prefix="/auth/jwt", tags=["auth"]
)
app.include_router(
fastapi_users.get_register_router(on_after_register), prefix="/auth", tags=["auth"]
)
app.include_router(
fastapi_users.get_reset_password_router(
SECRET, after_forgot_password=on_after_forgot_password
),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_verify_router(
SECRET, after_verification_request=after_verification_request
),
prefix="/auth",
tags=["auth"],
)
app.include_router(fastapi_users.get_users_router(), prefix="/users", tags=["users"])

View File

@@ -1,105 +0,0 @@
import databases
import sqlalchemy
from fastapi import FastAPI, Request
from fastapi_users import FastAPIUsers, models
from fastapi_users.authentication import JWTAuthentication
from fastapi_users.db import OrmarBaseUserModel, OrmarUserDatabase
DATABASE_URL = "sqlite:///test.db"
SECRET = "SECRET"
metadata = sqlalchemy.MetaData()
database = databases.Database(DATABASE_URL)
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB):
pass
class UserModel(OrmarBaseUserModel):
class Meta:
tablename = "users"
metadata = metadata
database = database
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
user_db = OrmarUserDatabase(UserDB, UserModel)
def on_after_register(user: UserDB, request: Request):
print(f"User {user.id} has registered.")
def on_after_forgot_password(user: UserDB, token: str, request: Request):
print(f"User {user.id} has forgot their password. Reset token: {token}")
def after_verification_request(user: UserDB, token: str, request: Request):
print(f"Verification requested for user {user.id}. Verification token: {token}")
jwt_authentication = JWTAuthentication(
secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/jwt/login"
)
app = FastAPI()
fastapi_users = FastAPIUsers(
user_db,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
UserDB,
)
app.include_router(
fastapi_users.get_auth_router(jwt_authentication), prefix="/auth/jwt", tags=["auth"]
)
app.include_router(
fastapi_users.get_register_router(on_after_register), prefix="/auth", tags=["auth"]
)
app.include_router(
fastapi_users.get_reset_password_router(
SECRET, after_forgot_password=on_after_forgot_password
),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_verify_router(
SECRET, after_verification_request=after_verification_request
),
prefix="/auth",
tags=["auth"],
)
app.include_router(fastapi_users.get_users_router(), prefix="/users", tags=["users"])
app.state.database = database
@app.on_event("startup")
async def startup() -> None:
database_ = app.state.database
if not database_.is_connected:
await database_.connect()
@app.on_event("shutdown")
async def shutdown() -> None:
database_ = app.state.database
if database_.is_connected:
await database_.disconnect()

View File

@@ -1,101 +0,0 @@
import databases
import sqlalchemy
from fastapi import FastAPI, Request
from fastapi_users import FastAPIUsers, models
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"
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB):
pass
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(UserDB, database, users)
def on_after_register(user: UserDB, request: Request):
print(f"User {user.id} has registered.")
def on_after_forgot_password(user: UserDB, token: str, request: Request):
print(f"User {user.id} has forgot their password. Reset token: {token}")
def after_verification_request(user: UserDB, token: str, request: Request):
print(f"Verification requested for user {user.id}. Verification token: {token}")
jwt_authentication = JWTAuthentication(
secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/jwt/login"
)
app = FastAPI()
fastapi_users = FastAPIUsers(
user_db,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
UserDB,
)
app.include_router(
fastapi_users.get_auth_router(jwt_authentication), prefix="/auth/jwt", tags=["auth"]
)
app.include_router(
fastapi_users.get_register_router(on_after_register), prefix="/auth", tags=["auth"]
)
app.include_router(
fastapi_users.get_reset_password_router(
SECRET, after_forgot_password=on_after_forgot_password
),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_verify_router(
SECRET, after_verification_request=after_verification_request
),
prefix="/auth",
tags=["auth"],
)
app.include_router(fastapi_users.get_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()

View File

@@ -1,88 +0,0 @@
from fastapi import FastAPI, Request
from fastapi_users import FastAPIUsers, models
from fastapi_users.authentication import JWTAuthentication
from fastapi_users.db import TortoiseBaseUserModel, TortoiseUserDatabase
from tortoise.contrib.fastapi import register_tortoise
from tortoise.contrib.pydantic import PydanticModel
DATABASE_URL = "sqlite://./test.db"
SECRET = "SECRET"
class UserModel(TortoiseBaseUserModel):
pass
class User(models.BaseUser):
pass
class UserCreate(models.BaseUserCreate):
pass
class UserUpdate(User, models.BaseUserUpdate):
pass
class UserDB(User, models.BaseUserDB, PydanticModel):
class Config:
orm_mode = True
orig_model = UserModel
user_db = TortoiseUserDatabase(UserDB, UserModel)
app = FastAPI()
register_tortoise(
app,
db_url=DATABASE_URL,
modules={"models": ["full_tortoise"]},
generate_schemas=True,
)
def on_after_register(user: UserDB, request: Request):
print(f"User {user.id} has registered.")
def on_after_forgot_password(user: UserDB, token: str, request: Request):
print(f"User {user.id} has forgot their password. Reset token: {token}")
def after_verification_request(user: UserDB, token: str, request: Request):
print(f"Verification requested for user {user.id}. Verification token: {token}")
jwt_authentication = JWTAuthentication(
secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/jwt/login"
)
fastapi_users = FastAPIUsers(
user_db,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
UserDB,
)
app.include_router(
fastapi_users.get_auth_router(jwt_authentication), prefix="/auth/jwt", tags=["auth"]
)
app.include_router(
fastapi_users.get_register_router(on_after_register), prefix="/auth", tags=["auth"]
)
app.include_router(
fastapi_users.get_reset_password_router(
SECRET, after_forgot_password=on_after_forgot_password
),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_verify_router(
SECRET, after_verification_request=after_verification_request
),
prefix="/auth",
tags=["auth"],
)
app.include_router(fastapi_users.get_users_router(), prefix="/users", tags=["users"])

32
docs/src/user_manager.py Normal file
View File

@@ -0,0 +1,32 @@
from typing import Optional
from fastapi import Depends, Request
from fastapi_users import BaseUserManager
from .db import get_user_db
from .models import UserCreate, UserDB
SECRET = "SECRET"
class UserManager(BaseUserManager[UserCreate, UserDB]):
user_db_model = UserDB
reset_password_token_secret = SECRET
verification_token_secret = SECRET
async def on_after_register(self, user: UserDB, request: Optional[Request] = None):
print(f"User {user.id} has registered.")
async def on_after_forgot_password(
self, user: UserDB, token: str, request: Optional[Request] = None
):
print(f"User {user.id} has forgot their password. Reset token: {token}")
async def on_after_request_verify(
self, user: UserDB, token: str, request: Optional[Request] = None
):
print(f"Verification requested for user {user.id}. Verification token: {token}")
def get_user_manager(user_db=Depends(get_user_db)):
yield UserManager(user_db)

View File

@@ -1,41 +0,0 @@
# Helpers
**FastAPI Users** provides some helper functions to perform some actions programmatically. They are available from your `FastAPIUsers` instance.
## Create user
Create a user.
```py
regular_user = await fastapi_users.create_user(
UserCreate(
email="king.arthur@camelot.bt",
password="guinevere",
)
)
superuser = await fastapi_users.create_user(
UserCreate(
email="king.arthur@camelot.bt",
password="guinevere",
is_superuser=True,
)
)
```
## Verify user
Verify a user.
```py
verified_user = await fastapi_users.verify_user(non_verified_user)
assert verified_user.is_verified is True
```
## Get user
Retrieve a user by e-mail.
```py
user = await fastapi_users.get_user("king.arthur@camelot.bt")
```

View File

@@ -31,39 +31,53 @@ markdown_extensions:
permalink: true
- admonition
- codehilite
- pymdownx.superfences
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:mermaid2.fence_mermaid
- pymdownx.tasklist
- pymdownx.tabbed
plugins:
- search
- mermaid2:
arguments:
theme: |
^(JSON.parse(window.localStorage.getItem('/.__palette')).index == 1) ? 'dark' : 'light'
extra_javascript:
- https://unpkg.com/mermaid/dist/mermaid.min.js
nav:
- About: index.md
- installation.md
- Configuration:
- configuration/model.md
- Databases:
- configuration/overview.md
- configuration/models.md
- Database adapters:
- configuration/databases/sqlalchemy.md
- configuration/databases/mongodb.md
- configuration/databases/tortoise.md
- configuration/databases/ormar.md
- Authentication:
- Authentication backends:
- Introduction: configuration/authentication/index.md
- configuration/authentication/jwt.md
- configuration/authentication/cookie.md
- configuration/user-manager.md
- Routers:
- Introduction: configuration/routers/index.md
- configuration/routers/auth.md
- configuration/routers/register.md
- configuration/routers/verify.md
- configuration/routers/reset.md
- configuration/routers/users.md
- configuration/routers/verify.md
- configuration/password-validation.md
- configuration/full-example.md
- configuration/oauth.md
- Usage:
- usage/flow.md
- usage/routes.md
- usage/current-user.md
- usage/helpers.md
- Migration:
- migration/08_to_1x.md
- migration/1x_to_2x.md

View File

@@ -8,6 +8,7 @@ pytest-asyncio
flake8-docstrings
mkdocs
mkdocs-material
mkdocs-mermaid2-plugin
black
mypy
codecov