New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add routes for user activation #403
Changes from 2 commits
d90893f
c90a62f
ba9a732
d0158d6
b95f988
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,26 @@ | ||
# Register routes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should revert those changes. (except grammar fixes of courses) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, agreed |
||
|
||
The register router will generate a `/register` route to allow a user to create a new account. | ||
The register router will generate a `/register` route to allow a user to create a new account, and optionally an `/activate` route to allow activation of the user account. | ||
|
||
Check the [routes usage](../../usage/routes.md) to learn how to use them. | ||
|
||
## User activation | ||
|
||
Each user account has an associated `is_active` attribute. This determines whether or not the user is activated. Only activated users can log in and request a password reset. Check the [user model](../../configuration/model.md) to learn more about the attributes of a user. | ||
|
||
By default, any users created by calling the `/register` route are activated upon initialisation. | ||
|
||
If user verification is required, then the `activation_callback` must be supplied to `get_register_router`, the register router generator. When this callback is supplied, newly registered users are not activated by default, and a corresponding `/activate` route is created. | ||
|
||
User activation then proceeds as follows: | ||
|
||
* The user registers using the `/register` route. | ||
* The `activation_callback` is called, with a unique activation token in the request body. | ||
* The user is activated if this token is supplied as a parameter to the `/activate` route before token expiry (after `activation_token_lifetime_seconds`). | ||
* Finally, the `after_register` callback is called once the user is activated. | ||
|
||
Email verification can be implemented within `activation_callback`, emailing the user with the corresponding `/activate` URL. An example can be found [below](#activation-callback). | ||
|
||
## Setup | ||
|
||
```py | ||
|
@@ -30,11 +47,34 @@ app.include_router( | |
prefix="/auth", | ||
tags=["auth"], | ||
) | ||
|
||
``` | ||
`get_register_router` | ||
|
||
Parameters: | ||
|
||
* `after_register: Optional[Callable[[models.UD, Request], None]] = None` | ||
|
||
Optional callback which takes a **user** (`models.UD`) and a **request** (`Request`) and is called upon user activation. | ||
|
||
* `activation_callback: Optional[Callable[[models.UD, str, Request], None]] = None` | ||
|
||
Optional callback which takes a **user** (`models.UD`), a **token** (`str`) and a **request** (`Request`). If supplied, the user is not activated by default, and can be activated by passing the token to the activate route. If not supplied, no activation step occurs and the user is activated by default. | ||
|
||
* `activation_token_secret: str = None`: | ||
|
||
Cryptographic secret to encode activation token. Required if `activation_callback` supplied. | ||
|
||
* `activation_token_lifetime_seconds: int = 3600`: | ||
|
||
Lifetime of the activation token in seconds, if activation_callback supplied. **Defaults to 3600**. | ||
|
||
## After register callback | ||
|
||
## After register | ||
You can provide a custom function, `after_register` to be called after a user is successfully activated. This can occur either upon registration if no `activation_callback` is specified, or instead upon activation otherwise. This is called with **two arguments**: | ||
|
||
You can provide a custom function to be called after a successful registration. It is called with **two argument**: the **user** that has just registered, and the original **`Request` object**. | ||
* The `user` that has just registered | ||
* The original `Request` object | ||
|
||
Typically, you'll want to **send a welcome e-mail** or add it to your marketing analytics pipeline. | ||
|
||
|
@@ -52,3 +92,42 @@ app.include_router( | |
tags=["auth"], | ||
) | ||
``` | ||
|
||
## Activation callback | ||
|
||
You can optionally provide an `activation_callback` for [custom user activation](#user-activation). It is called with **three arguments**: | ||
|
||
* The `user` that has just registered | ||
* The activation `token` associated with that user | ||
* The original `Request` object | ||
|
||
You must also supply `activation_token_secret` for this case - a cryptographic secret used to sign the token. | ||
|
||
`get_register_router` automatically initializes the `/activate` route when `activation_token_secret` and `activation_callback` are supplied. | ||
|
||
A token will be passed to your `activation_callback`. This token can be used to create a url to the `/activate` router, which will in turn activate a user. | ||
|
||
Typically, you'll want to **send an activation e-mail** which contains this URL. | ||
|
||
Example: | ||
|
||
```py | ||
def activation_callback( | ||
created_user: Type[models.BaseUserCreate], | ||
token: str, | ||
request: Request | ||
): | ||
confirm_url = request.url_for("activate", token=token) | ||
print('User {user.id} is to be activated. Visit {confirm_url} to activate.') | ||
|
||
def on_after_register(user: UserDB, request: Request): | ||
print(f"User {user.id} has registered.") | ||
|
||
app.include_router( | ||
fastapi_users.get_register_router( | ||
after_register = on_after_register, | ||
activation_callback = activation_callback, | ||
activation_token_secret = 'teststring' | ||
), prefix="/auth", tags=["auth"] | ||
) | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,7 +44,7 @@ Logout the authenticated user against the method named `name`. Check the corresp | |
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should revert those changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, agreed |
||
### `POST /register` | ||
|
||
Register a new user. Will call the `after_register` [handler](../configuration/routers/register.md#after-register) on successful registration. | ||
Register a new user. Will call the `after_register` [handler](../configuration/routers/register.md#after-register) on successful activation. | ||
|
||
!!! abstract "Payload" | ||
```json | ||
|
@@ -54,6 +54,8 @@ Register a new user. Will call the `after_register` [handler](../configuration/r | |
} | ||
``` | ||
|
||
If `activation_callback` not supplied: | ||
|
||
!!! success "`201 Created`" | ||
```json | ||
{ | ||
|
@@ -64,6 +66,18 @@ Register a new user. Will call the `after_register` [handler](../configuration/r | |
} | ||
``` | ||
|
||
If `activation_callback` supplied: | ||
|
||
!!! success "`201 Created`" | ||
```json | ||
{ | ||
"id": "57cbb51a-ab71-4009-8802-3f54b4f2e23", | ||
"email": "king.arthur@camelot.bt", | ||
"is_active": false, | ||
"is_superuser": false | ||
} | ||
``` | ||
|
||
!!! fail "`422 Validation Error`" | ||
|
||
!!! fail "`400 Bad Request`" | ||
|
@@ -74,6 +88,49 @@ Register a new user. Will call the `after_register` [handler](../configuration/r | |
"detail": "REGISTER_USER_ALREADY_EXISTS" | ||
} | ||
``` | ||
## Activate router | ||
|
||
### `POST /activate` | ||
|
||
Activate a new user. Will call the `after_register` [handler](../configuration/routers/register.md#activation-callback) on successful activation. | ||
|
||
!!! abstract "Payload" | ||
```json | ||
{ | ||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNmJhYTI4OWYtOGE0OC00ZGMwLThiM2UtMmViOTgwOGIxODQ2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphY3RpdmF0ZSIsImV4cCI6MTYwNjMyOTA5OH0.-HWG4j5_ygtRahiiBcwLfMHOFq_ydcJegHyK65ppchs" | ||
} | ||
``` | ||
|
||
!!! success "`202 Accepted`" User has been activated | ||
|
||
!!! fail "`400 Bad Request`" | ||
The link has already been used. | ||
|
||
```json | ||
{ | ||
"detail": "ACTIVATE_USER_LINK_USED" | ||
} | ||
``` | ||
|
||
!!! fail "`400 Bad Request`" | ||
The token expired. | ||
|
||
```json | ||
{ | ||
"detail": "ACTIVATE_USER_TOKEN_EXPIRED" | ||
} | ||
``` | ||
|
||
!!! fail "`400 Bad Request`" | ||
Bad token. | ||
|
||
```json | ||
{ | ||
"detail": "ACTIVATE_USER_BAD_TOKEN" | ||
} | ||
``` | ||
|
||
!!! fail "`422 Validation Error`" | ||
|
||
## Reset password router | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should revert those changes. (except grammar fixes of courses)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, agreed