core: Add example invitation blueprint (#17661)

* Add flows-invitation-enrollment.yaml blueprint example, make serializer add default anonymous user in blueprint context

* Add tests

* fix linting

* Update invitations docs

* Use custom attributes instead of fixed_data

* remove clutter

* Reworks the invitations doc to new styling standards

* Apply suggestions

* fix field

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Add manual steps for invitation creation

* add both options at the beginning

* use serializer.context in InvitationViewSet.perform_create

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* add description to bluprint

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* tweaks to structure and formatting

* Optimised images with calibre/image-actions

* Update website/docs/users-sources/user/invitations.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* fix linting

* imports

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* less branch

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* gen

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tana@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
This commit is contained in:
Marcelo Elizeche Landó
2025-11-06 16:29:04 -03:00
committed by GitHub
parent 221b8431ef
commit 9802d4bcdd
7 changed files with 672 additions and 24 deletions
+18 -1
View File
@@ -2,11 +2,15 @@
from django_filters.filters import BooleanFilter
from django_filters.filterset import FilterSet
from guardian.shortcuts import get_anonymous_user
from rest_framework.serializers import PrimaryKeyRelatedField
from rest_framework.viewsets import ModelViewSet
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.groups import PartialUserSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import JSONDictField, ModelSerializer
from authentik.core.models import User
from authentik.flows.api.flows import FlowSerializer
from authentik.flows.api.stages import StageSerializer
from authentik.stages.invitation.models import Invitation, InvitationStage
@@ -49,6 +53,16 @@ class InvitationSerializer(ModelSerializer):
fixed_data = JSONDictField(required=False)
flow_obj = FlowSerializer(read_only=True, required=False, source="flow")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
self.fields["created_by"] = PrimaryKeyRelatedField(
queryset=User.objects.all(),
required=False,
allow_null=True,
default=get_anonymous_user(),
)
class Meta:
model = Invitation
fields = [
@@ -73,4 +87,7 @@ class InvitationViewSet(UsedByMixin, ModelViewSet):
filterset_fields = ["name", "created_by__username", "expires", "flow__slug"]
def perform_create(self, serializer: InvitationSerializer):
serializer.save(created_by=self.request.user)
kwargs = {}
if SERIALIZER_CONTEXT_BLUEPRINT not in serializer.context:
kwargs["created_by"] = self.request.user
serializer.save(**kwargs)
+19
View File
@@ -7,6 +7,7 @@ from django.utils.http import urlencode
from guardian.shortcuts import get_anonymous_user
from rest_framework.test import APITestCase
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.markers import StageMarker
from authentik.flows.models import FlowDesignation, FlowStageBinding
@@ -14,6 +15,7 @@ from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests import FlowTestCase
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.stages.invitation.api import InvitationSerializer
from authentik.stages.invitation.models import Invitation, InvitationStage
from authentik.stages.invitation.stage import (
PLAN_CONTEXT_INVITATION_TOKEN,
@@ -171,3 +173,20 @@ class TestInvitationsAPI(APITestCase):
)
self.assertEqual(response.status_code, 201)
self.assertEqual(Invitation.objects.first().created_by, self.user)
def test_invite_create_blueprint_context(self):
"""Test Invitations creation via blueprint context"""
flow = create_test_flow(FlowDesignation.ENROLLMENT)
data = {
"name": "test-blueprint-invitation",
"flow": flow.pk.hex,
"single_use": True,
"fixed_data": {"email": "test@example.com"},
}
serializer = InvitationSerializer(data=data, context={SERIALIZER_CONTEXT_BLUEPRINT: True})
self.assertTrue(serializer.is_valid())
invitation = serializer.save()
self.assertEqual(invitation.created_by, get_anonymous_user())
self.assertEqual(invitation.name, "test-blueprint-invitation")
self.assertEqual(invitation.fixed_data, {"email": "test@example.com"})
@@ -0,0 +1,396 @@
# Example - Invitation-based Enrollment Blueprint
#
# This blueprint demonstrates invitation-based user enrollment with support for
# internal and external user types, automatic group assignment, and user path organization.
#
# What this blueprint creates:
# - 3 enrollment flows:
# * External users flow (invitation-enrollment-flow-external)
# * Internal users flow (invitation-enrollment-flow-internal)
# * Internal users flow with automatic group assignment (invitation-enrollment-flow-internal-engineering)
# - 3 invitation stages (one for each flow)
# - Prompt fields for collecting user credentials and details (username, password, name, email)
# - 2 prompt stages (credentials and user details)
# - 3 user write stages configured for different user types and paths:
# * External users: user_type=external, path=users/external
# * Internal users: user_type=internal, path=users/internal
# * Engineering team: user_type=internal, path=users/internal/engineering, auto-assigned to engineering-team group
# - 1 user login stage
# - 1 example group (engineering-team)
# - 5 example invitations demonstrating different use cases
#
# For detailed documentation, see:
# https://docs.goauthentik.io/users-sources/user/invitations/
#
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "false"
name: Example - Invitation-based Enrollment
entries:
# Flow definition for external users
- identifiers:
slug: invitation-enrollment-flow-external
model: authentik_flows.flow
id: flow-external
attrs:
name: Invitation Enrollment Flow (External Users)
title: Welcome! Complete your enrollment
designation: enrollment
authentication: require_unauthenticated
# Flow definition for internal users
- identifiers:
slug: invitation-enrollment-flow-internal
model: authentik_flows.flow
id: flow-internal
attrs:
name: Invitation Enrollment Flow (Internal Users)
title: Welcome! Complete your enrollment
designation: enrollment
authentication: require_unauthenticated
# Flow definition for internal users with group assignment
- identifiers:
slug: invitation-enrollment-flow-internal-engineering
model: authentik_flows.flow
id: flow-internal-engineering
attrs:
name: Invitation Enrollment Flow (Internal - Engineering Team)
title: Welcome to the Engineering Team!
designation: enrollment
authentication: require_unauthenticated
# Invitation Stage for external users
- identifiers:
name: invitation-enrollment-invitation-external
id: invitation-stage-external
model: authentik_stages_invitation.invitationstage
attrs:
continue_flow_without_invitation: false
# Invitation Stage for internal users
- identifiers:
name: invitation-enrollment-invitation-internal
id: invitation-stage-internal
model: authentik_stages_invitation.invitationstage
attrs:
continue_flow_without_invitation: false
# Invitation Stage for internal engineering users
- identifiers:
name: invitation-enrollment-invitation-internal-engineering
id: invitation-stage-internal-engineering
model: authentik_stages_invitation.invitationstage
attrs:
continue_flow_without_invitation: false
# Prompt fields for user information
- id: prompt-field-username
model: authentik_stages_prompt.prompt
identifiers:
name: invitation-enrollment-field-username
attrs:
field_key: username
label: Username
type: username
required: true
placeholder: Username
placeholder_expression: false
order: 0
- identifiers:
name: invitation-enrollment-field-password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
field_key: password
label: Password
type: password
required: true
placeholder: Password
placeholder_expression: false
order: 1
- identifiers:
name: invitation-enrollment-field-password-repeat
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat
label: Password (repeat)
type: password
required: true
placeholder: Password (repeat)
placeholder_expression: false
order: 2
- identifiers:
name: invitation-enrollment-field-name
id: prompt-field-name
model: authentik_stages_prompt.prompt
attrs:
field_key: name
label: Name
type: text
required: true
placeholder: Name
placeholder_expression: false
order: 0
- identifiers:
name: invitation-enrollment-field-email
id: prompt-field-email
model: authentik_stages_prompt.prompt
attrs:
field_key: email
label: Email
type: email
required: true
placeholder: Email
placeholder_expression: false
order: 1
# Prompt stage for credentials
- identifiers:
name: invitation-enrollment-prompt-credentials
id: prompt-stage-credentials
model: authentik_stages_prompt.promptstage
attrs:
fields:
- !KeyOf prompt-field-username
- !KeyOf prompt-field-password
- !KeyOf prompt-field-password-repeat
# Prompt stage for user details
- identifiers:
name: invitation-enrollment-prompt-details
id: prompt-stage-details
model: authentik_stages_prompt.promptstage
attrs:
fields:
- !KeyOf prompt-field-name
- !KeyOf prompt-field-email
# User write stage for external users
- identifiers:
name: invitation-enrollment-user-write-external
id: user-write-stage-external
model: authentik_stages_user_write.userwritestage
attrs:
user_creation_mode: always_create
user_type: external
user_path_template: users/external
# User write stage for internal users
- identifiers:
name: invitation-enrollment-user-write-internal
id: user-write-stage-internal
model: authentik_stages_user_write.userwritestage
attrs:
user_creation_mode: always_create
user_type: internal
user_path_template: users/internal
# Example group for demonstrating group assignment
- identifiers:
name: engineering-team
id: group-engineering
model: authentik_core.group
attrs:
is_superuser: false
# User write stage for internal users with group assignment
- identifiers:
name: invitation-enrollment-user-write-internal-engineering
id: user-write-stage-internal-engineering
model: authentik_stages_user_write.userwritestage
attrs:
user_creation_mode: always_create
user_type: internal
user_path_template: users/internal/engineering
create_users_group: !KeyOf group-engineering
# User login stage
- identifiers:
name: invitation-enrollment-user-login
id: user-login-stage
model: authentik_stages_user_login.userloginstage
# Flow stage bindings for EXTERNAL users flow
- identifiers:
target: !KeyOf flow-external
stage: !KeyOf invitation-stage-external
order: 5
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: true
- identifiers:
target: !KeyOf flow-external
stage: !KeyOf prompt-stage-credentials
order: 10
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-external
stage: !KeyOf prompt-stage-details
order: 15
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-external
stage: !KeyOf user-write-stage-external
order: 20
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-external
stage: !KeyOf user-login-stage
order: 100
model: authentik_flows.flowstagebinding
# Flow stage bindings for INTERNAL users flow
- identifiers:
target: !KeyOf flow-internal
stage: !KeyOf invitation-stage-internal
order: 5
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: true
- identifiers:
target: !KeyOf flow-internal
stage: !KeyOf prompt-stage-credentials
order: 10
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-internal
stage: !KeyOf prompt-stage-details
order: 15
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-internal
stage: !KeyOf user-write-stage-internal
order: 20
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-internal
stage: !KeyOf user-login-stage
order: 100
model: authentik_flows.flowstagebinding
# Flow stage bindings for INTERNAL ENGINEERING users flow (with group assignment)
- identifiers:
target: !KeyOf flow-internal-engineering
stage: !KeyOf invitation-stage-internal-engineering
order: 5
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: true
- identifiers:
target: !KeyOf flow-internal-engineering
stage: !KeyOf prompt-stage-credentials
order: 10
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-internal-engineering
stage: !KeyOf prompt-stage-details
order: 15
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-internal-engineering
stage: !KeyOf user-write-stage-internal-engineering
order: 20
model: authentik_flows.flowstagebinding
- identifiers:
target: !KeyOf flow-internal-engineering
stage: !KeyOf user-login-stage
order: 100
model: authentik_flows.flowstagebinding
# Example invitations
# EXTERNAL USER INVITATIONS
# Example 1: Basic single-use invitation for external user
- identifiers:
name: example-external-basic-invitation
model: authentik_stages_invitation.invitation
id: invitation-external-basic
attrs:
flow: !KeyOf flow-external
single_use: false
fixed_data: {}
# Example 2: Multi-use invitation for external users with pre-filled email
- identifiers:
name: example-external-prefilled-email-invitation
model: authentik_stages_invitation.invitation
id: invitation-external-prefilled-email
attrs:
flow: !KeyOf flow-external
single_use: true
expires: "2028-12-31T23:59:59Z"
fixed_data:
email: "external@example.com"
# INTERNAL USER INVITATIONS
# Example 3: Single-use invitation for internal user with pre-filled fields
- identifiers:
name: example-internal-prefilled-invitation
model: authentik_stages_invitation.invitation
id: invitation-internal-prefilled
attrs:
flow: !KeyOf flow-internal
single_use: true
expires: "2028-12-31T23:59:59Z"
fixed_data:
name: "Jane Smith"
email: "jane.smith@company.com"
# Example 4: Long-term multi-use invitation for internal department
- identifiers:
name: example-internal-department-invitation
model: authentik_stages_invitation.invitation
id: invitation-internal-department
attrs:
flow: !KeyOf flow-internal
single_use: false
expires: "2028-12-31T23:59:59Z"
fixed_data:
attributes:
department: "Engineering"
team: "Backend"
# Example 5: Invitation with automatic group assignment
- identifiers:
name: example-engineering-team-invitation
model: authentik_stages_invitation.invitation
id: invitation-engineering-team
attrs:
flow: !KeyOf flow-internal-engineering
single_use: false
expires: "2028-12-31T23:59:59Z"
fixed_data:
attributes:
department: "Engineering"
# Note: Group assignment works by using a flow with a UserWriteStage that has
# 'create_users_group' configured. See example 5 above - users enrolling via
# the 'invitation-enrollment-flow-internal-engineering' flow will automatically
# be added to the 'engineering-team' group.
#
# Groups cannot be set directly in invitation fixed_data because they require
# database relationships that must be established after user creation.
+4
View File
@@ -14981,6 +14981,10 @@
"additionalProperties": true,
"title": "Fixed data"
},
"created_by": {
"type": "integer",
"title": "Created by"
},
"single_use": {
"type": "boolean",
"title": "Single use",
@@ -135,6 +135,21 @@ For example:
- authentik_blueprints.view_blueprintinstance
```
## `authentik_stages_invitation.invitation`:ak-version[2025.12]
The `created_by` field can be set to a user. If no value is given, the internal hidden anonymous user is used.
For example:
```yaml
# [...]
- model: authentik_stages_invitation.invitation
identifiers:
name: test-invitation
attrs:
created_by: !Find [authentik_core.user, [username, "my-admin-user"]]
```
## `authentik_policies.policybinding`
### Required fields
Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 70 KiB

+220 -23
View File
@@ -1,53 +1,250 @@
---
title: Invitations
description: "Learn how to create an invitation URL for new users to enroll."
toc_max_heading_level: 4
---
Invitations are another way to create a user, by inviting someone to join your authentik instance, as a new user. With invitations, you can either email an enrollment invitation URL to one or more specific recipients with pre-defined credentials, or you can email a URL to users, who can then log in and define their own credentials.
Invitations are another way to create a user, by inviting someone to join your authentik instance as a new user. With invitations, you can either email an enrollment invitation URL to one or more specific recipients with pre-defined credentials, or you can email a URL to users, who can then log in and define their own credentials.
You can configure invitations either by:
- using [pre-built blueprints](#use-pre-built-blueprints-to-configure-invitations) (recommended for quick setup).
- [manually creating flows and stages](#manual-setup-without-blueprints) (for custom configurations).
:::info
You can also create a policy to see if the invitation was ever used.
You can also create a [policy](../../../customize/policies/) to see if the invitation was ever used.
:::
## Create an invitation
## Use pre-built blueprints to configure invitations
The fastest way to create an invitation is to use our pre-defined `default-enrollment-flow` that has the necessary stages and prompts already included.
The fastest way to configure invitations in authentik is to use our pre-defined blueprints that have the necessary flows, stages, and prompts already included.
**Step 1. Download the `default-enrollment-flow` file**
### Step 1. Download a blueprint
To download the `default-enrollment-flow` file, run this command:
We have two pre-defined blueprints, the`Example - Invitation-based Enrollment` blueprint and the `Example - Enrollment (2 Stage)`blueprint.
```shell
wget https://goauthentik.io/blueprints/example/flows-enrollment-2-stage.yaml
```
- #### Option 1: Download the `Example - Invitation-based Enrollment` blueprint (Recommended)
Alternatively, use this [link](/blueprints/example/flows-enrollment-2-stage.yaml) to view and save the file.
This blueprint provides several examples of how to configure different invitation features and serves as a helpful starting point:
- Separate flows for external and internal users
- An example of [automatic group assignment](#automatic-group-assignment) (creates an example group called `engineering-team`)
- [User path organization](#user-paths)
- Five example invitations demonstrating different use cases
**Step 2. Import the `default-enrollment-flow` file**
Download the `Example - Invitation-based Enrollment` blueprint by running this command:
In authentik, navigate to the Admin UI, and then click **Flows** in the left navigation pane.
```shell
wget https://goauthentik.io/blueprints/example/flows-invitation-enrollment.yaml
```
At the top of the Flows page, click **Import**, and then select the `flows-enrollment-2-stage.yaml` file that you just downloaded.
Alternatively, use this [link](/blueprints/example/flows-invitation-enrollment.yaml) to view and save the file.
**Step 3. Create the invitation object**
- #### Option 2: Download the `Example - Enrollment (2 Stage)` blueprint
In the Admin UI, navigate to **Directory --> Invitations**, and then click **Create** to open the **Create Invitation** box. Define the following fields:
For a simpler invitation flow that doesn't include separate flows for internal/external user types, [automatic group assignment](#automatic-group-assignment), and [set user paths](#user-paths), download the `Example - Enrollment (2 Stage)` blueprint by running this command:
```shell
wget https://goauthentik.io/blueprints/example/flows-enrollment-2-stage.yaml
```
Alternatively, use this [link](/blueprints/example/flows-enrollment-2-stage.yaml) to view and save the file.
### Step 2. Import the blueprint file
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages** > **Flows** and click **Import**.
3. Click **Choose file**, select the blueprint file that you downloaded, and then click **Import**.
### Step 3. Create the invitation object
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Directory** > **Invitations** and click **Create**.
The Create Invitation box appears.
![Create an invitation modal](./create_invite.png)
Configure the following settings:
- **Name**: provide a name for your invitation object.
- **Expires**: select a date for when you want the invitation to expire.
- **Flow**: in the drop-down menu, select the **default-enrollment-flow** Flow.
- **Custom attributes**: (_optional_) enter optional key/value pairs here, to pre-define any information about the user that you will invite to enroll. The data entered here is considered as a variable, specifically the `context['prompt_data']` variable. This data is read by the context flow's [prompt stage](../../add-secure-apps/flows-stages/stages/prompt/index.md) in an expression policy.
- **Flow**: In the drop-down menu, select the enrollment flow to use (`invitation-enrollment-flow-external`, `invitation-enrollment-flow-internal`, `invitation-enrollment-flow-internal-engineering`, or ` default-enrollment-flow` depending on which blueprint you used).
- **Custom attributes**: (_optional_) Enter JSON or YAML to pre-fill user information. This data is merged with the user's input during enrollment.
![Create an invitation modal box](./create_invite.png)
<details>
<summary>Example custom attributes:</summary>
- **Single use**: specify whether or not you want the invitation to expire after a single use.
**Pre-fill email only (JSON)**:
Click **Save** to save the new invitation and close the box and return to the **Invitations** page.
```json
{
"email": "user@example.com"
}
```
**Step 3. Email the invitation**
**Pre-fill multiple fields (YAML)**:
On the **Invitations** page, click the chevron beside your new invitation, to expand the details. The **Link to use the invitation** displays with the URL. Copy the URL and send it in an email to the people you want to invite to enroll.
```yaml
name: Jane Smith
email: jane.smith@company.com
```
**Pre-fill with custom attributes (JSON)**:
```json
{
"name": "John Doe",
"email": "john@example.com",
"attributes": {
"department": "Engineering",
"employee_type": "contractor",
"start_date": "2025-01-15"
}
}
```
</details>
:::info
The field keys (e.g., `email`, `name`) must match the field keys configured in your flow's [prompt stage](../../add-secure-apps/flows-stages/stages/prompt/index.md).
:::
- **Single use**: Specify whether the invitation should expire after a single use.
- Enable for invitations sent to specific individuals.
- Disable for invitations shared with multiple people (e.g., department onboarding links).
3. Click **Save**.
### Step 4. Share the invitation
On the **Invitations** page, click the chevron beside your new invitation to expand the details. The **Link to use the invitation** displays with the URL.
Copy the URL and send it in an email to the people you want to invite to enroll.
The invitation link format is:
```
https://authentik.company/if/flow/<flow-slug>/?itoken=<invitation-uuid>
```
## Manual setup (without blueprints)
If you prefer to create your invitation flow manually instead of using a blueprint, follow these steps:
### Step 1: Create an Invitation stage
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages** > **Stages** and click **Create**.
3. Select **Invitation Stage** from the stage type list.
4. Configure the stage:
- **Name**: Provide a descriptive name (e.g., `enrollment-invitation-stage`)
- **Continue flow without invitation**:
- Set to `false` if you want to require a valid invitation token (recommended for invitation-only flows).
- Set to `true` if you want to allow both invited and non-invited users to use the same enrollment flow.
5. Click **Create**.
:::info
The **Continue flow without invitation** setting determines whether users can proceed through the flow without a valid invitation token. When set to `false`, only users with valid invitation links can complete enrollment.
:::
### Step 2: Create or modify an Enrollment flow
1. Navigate to **Flows and Stages** > **Flows**.
2. Either create a new flow or edit an existing enrollment flow:
- **Name**: Provide a descriptive name.
- **Title**: Enter the title shown to users during enrollment.
- **Slug**: Enter a unique identifier (e.g., `invitation-enrollment`).
- **Designation**: Must be set to **Enrollment**.
- **Authentication**: Set to **Require unauthenticated** (users shouldn't be logged in to enroll).
### Step 3: Bind the Invitation stage to the flow
1. In your enrollment flow, go to the **Stage Bindings** tab.
2. Click **Bind Stage** and select your invitation stage.
3. Configure the binding:
- **Order**: Set to a low number (e.g., `5` or `10`) so it evaluates early in the flow.
- **Evaluate on plan**: Enable this option so the invitation is validated when the flow starts.
- **Re-evaluate policies**: Enable this to ensure policies are checked.
4. Add other necessary stages to your flow (in order):
- **Prompt Stage** for collecting credentials (username, password)
- **Prompt Stage** for collecting user details (name, email)
- **User Write Stage** to create the user account
- **User Login Stage** to log the user in after enrollment
### Step 4: Create invitations
Now you can create invitations that reference your custom flow. Follow the steps in [Create the invitation object](#step-3-create-the-invitation-object) above.
## Advanced features
### Automatic group assignment
To automatically add users to a group when they enroll via invitation, you need to configure the enrollment flow's User Write Stage:
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages** > **Stages**.
3. Create or edit a **User Write Stage** used by your enrollment flow.
4. Set **Create users group** to your desired group.
5. All users enrolling through that flow will automatically be added to the selected group.
:::info
Groups cannot be set directly in invitation custom attributes because they require database relationships. They must be configured at the flow/stage level.
:::
### User paths
[User paths](user_ref.mdx#path) organize users in a directory structure (e.g., `users/external`, `users/internal/engineering`). To configure user paths:
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages** > **Stages**.
3. Create or edit a **User Write Stage** used by your enrollment flow.
4. Set **User path template** to your desired path.
5. All users enrolling through that flow will be created under that path.
### Expression policies with invitations
You can use [expression policies](../../../customize/policies/expression/) to make decisions based on invitation data:
```python
# Check if user was invited
return context.get('invitation_in_effect', False)
# Access invitation data
invitation = context.get('invitation')
if invitation:
return invitation.fixed_data.get('department') == 'Engineering'
# Access pre-filled prompt data
prompt_data = context.get('prompt_data', {})
return prompt_data.get('email', '').endswith('@example.com')
```
## Troubleshooting
### "Permission denied" error for external users
**Problem**: External user sees "Interface can only be accessed by internal users" after enrollment.
**Solution**: Configure a Default Application in your brand settings (System → Brands) so external users have somewhere to go after login.
### Invitation not working
Possible causes:
- Invitation has expired (check the expiration date)
- Single-use invitation has already been used
- Flow slug doesn't match the invitation's configured flow
- Invitation stage is not bound to the flow
### Pre-filled Data Not Appearing
Possible causes:
- Field keys in custom attributes don't match your prompt field keys
- Prompt fields are marked as `placeholder_expression: true`
- Invitation stage is not evaluated before prompt stages in the flow
:::info Invitation links validity
Be aware that when an authentik administrator or any other user creates an invitation link, that link remains valid even if the administrator is deactivated or has permissionss revoked. However, if the user who created the link is deleted and removed from the authentik system, the link is also deleted.
Be aware that when an authentik administrator or any other user creates an invitation link, that link remains valid even if the administrator is deactivated or has permissions revoked. However, if the user who created the link is deleted and removed from the authentik system, the link is also deleted.
:::