Skip to content

Commit 196e27a

Browse files
authored
docs: add Actions documentation (#1454)
* docs: add Actions documentation * docs: keep Actions guidance self-contained * docs: document sign-in identifier updates in post sign-in action
1 parent b83fbba commit 196e27a

17 files changed

Lines changed: 621 additions & 25 deletions

docs/developers/README.mdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ In order to provide convenience to our users, Logto offers a series of commonly
88

99
```mdx-code-block
1010
import DocCardList from '@theme/DocCardList';
11+
import Action from '@site/src/assets/developer.svg';
1112
import JwtClaims from '@site/docs/developers/assets/icons/jwt-claims.svg';
1213
import UserImpersonation from '@site/docs/developers/assets/icons/role.svg';
1314
import Key from '@site/docs/developers/assets/icons/key.svg';
@@ -35,6 +36,15 @@ import Settings from '@site/docs/developers/assets/icons/gear.svg';
3536
icon: <JwtClaims />,
3637
}
3738
},
39+
{
40+
type: 'link',
41+
label: 'Actions',
42+
href: '/developers/actions',
43+
description: 'Run synchronous custom code in the authentication flow to migrate or enrich users.',
44+
customProps: {
45+
icon: <Action />,
46+
}
47+
},
3848
{
3949
type: 'link',
4050
label: 'User impersonation',

docs/developers/actions/README.mdx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
sidebar_position: 4
3+
---
4+
5+
# Actions
6+
7+
Logto Actions let you run trusted JavaScript at specific points in the authentication flow. An action runs synchronously: the authentication request waits for the script, and the script result can update the user or determine whether the flow continues.
8+
9+
Actions are useful when the decision must happen inside the authentication flow. Common use cases include:
10+
11+
- Migrating users and passwords from a legacy identity system when they first sign in.
12+
- Refreshing user profile or application-specific data before Logto completes a sign-in.
13+
- Calling an external service and applying its result to the Logto user.
14+
15+
:::note
16+
Actions are available in Logto OSS and Logto Cloud Enterprise plans.
17+
:::
18+
19+
:::warning
20+
Action scripts can affect authentication and modify user data. Only trusted administrators should be allowed to view, create, edit, test, enable, or delete them.
21+
22+
In self-hosted deployments, action scripts run in a virtual machine inside the Logto process. Treat them as trusted server-side code, not as a security boundary for untrusted code.
23+
:::
24+
25+
## How Actions fit into sign-in \{#how-actions-fit-into-sign-in}
26+
27+
Logto currently provides two action types:
28+
29+
```mermaid
30+
flowchart LR
31+
A["Password sign-in attempt"] --> B{"Local password is valid?"}
32+
B -->|"Yes"| E["Continue authentication"]
33+
B -->|"No"| C["Post first-factor verification Action"]
34+
C -->|"Credentials accepted"| D["Create or update user and save local password"]
35+
C -->|"Declined or failed"| X["Reject invalid credentials"]
36+
D --> E
37+
E --> F["Complete MFA (if required)"]
38+
F --> G["Post sign-in Action"]
39+
G --> H["Complete sign-in and issue tokens"]
40+
```
41+
42+
| Action type | When it runs | What it can do |
43+
| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
44+
| [Post first-factor verification](/developers/actions/post-first-factor-verification) | During a password sign-in, only after Logto's local password verification fails. It does not run when the local password is valid. | Verify the submitted credentials against a legacy system, then create a new Logto user or update an existing user and migrate the submitted password. |
45+
| [Post sign-in](/developers/actions/post-sign-in) | After the user has completed all authentication factors, including MFA when required, and before Logto completes the sign-in and issues tokens. | Update and enrich the existing Logto user using the final sign-in context. |
46+
47+
Both action types run only for `SignIn` interactions in the Experience API. Post first-factor verification applies only to password sign-in; Post sign-in is independent of the authentication method.
48+
49+
## Script model \{#script-model}
50+
51+
Each action type has one configuration and one JavaScript entry function named `runAction`:
52+
53+
```js
54+
const runAction = async ({ event, environmentVariables = {} }) => {
55+
// Inspect the event, optionally fetch external data, and return
56+
// a result supported by this action type.
57+
};
58+
```
59+
60+
The payload contains:
61+
62+
- `event`: The production authentication event. Its shape depends on the action type.
63+
- `environmentVariables`: The string values configured for this action. These values are passed through the function payload; they are not available through `process.env`.
64+
65+
The editor provides type information, but the saved script is executed as JavaScript. The script may be asynchronous and can use the injected `fetch` function to call external HTTPS APIs. It cannot import packages or access Node.js globals such as `require` or `process`.
66+
67+
The supported result is different for each action type; see the corresponding reference page before enabling an Action.
68+
69+
## Actions and Webhooks \{#actions-and-webhooks}
70+
71+
Actions and [Webhooks](/developers/webhooks) serve different purposes:
72+
73+
| | Actions | Webhooks |
74+
| ------------------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------- |
75+
| Execution | Synchronous and inline with authentication | Asynchronous and outside the authentication request |
76+
| Can affect the current authentication flow | Yes | No |
77+
| Can modify a user from its result | Yes, using the supported user patch | Not directly; the receiver can call the Management API separately |
78+
| Event coverage | Selected authentication points | A broad set of interaction and data-change events |
79+
| Typical use | Credential migration, pre-token profile enrichment | Notifications, downstream synchronization, analytics |
80+
81+
Keep asynchronous work in Webhooks. Use an Action only when Logto needs the result before authentication can continue.
82+
83+
## Next steps \{#next-steps}
84+
85+
- [Configure and test Actions](/developers/actions/configure-and-test-actions)
86+
- [Migrate users on password sign-in](/developers/actions/post-first-factor-verification)
87+
- [Enrich a user after sign-in](/developers/actions/post-sign-in)
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
id: configure-and-test-actions
3+
title: Configure and test Actions
4+
sidebar_label: Configure and test Actions
5+
sidebar_position: 2
6+
---
7+
8+
# Configure and test Actions
9+
10+
## Create an Action \{#create-an-action}
11+
12+
1. Navigate to <CloudLink to="/actions">Console > Actions</CloudLink>.
13+
2. Select **Post first-factor verification** or **Post sign-in**.
14+
3. Implement the `runAction` function in the script editor.
15+
4. Under **Data source**, review the event and result types, configure environment variables, and find an example for fetching external data.
16+
5. Under **Test context**, adjust the sample event and run the script.
17+
6. Under **Settings**, enable the Action and choose its script-error behavior.
18+
7. Save the Action.
19+
20+
Only a saved and enabled Action runs in production.
21+
22+
## Implement `runAction` \{#implement-runaction}
23+
24+
Keep the entry function name as `runAction`. It receives a single payload object:
25+
26+
```js
27+
const runAction = async ({ event, environmentVariables = {} }) => {
28+
return;
29+
};
30+
```
31+
32+
To decline or continue without a user update, return the no-op value supported by the specific action type.
33+
34+
### Fetch external data \{#fetch-external-data}
35+
36+
Use the injected `fetch` function to call an external API. For example, a Post sign-in Action can fetch a user profile:
37+
38+
```js
39+
const runAction = async ({ event, environmentVariables = {} }) => {
40+
const response = await fetch(environmentVariables.PROFILE_API_URL, {
41+
headers: {
42+
authorization: `Bearer ${environmentVariables.PROFILE_API_TOKEN}`,
43+
},
44+
});
45+
46+
if (!response.ok) {
47+
throw new Error(`Profile API returned ${response.status}`);
48+
}
49+
50+
const profile = await response.json();
51+
52+
return {
53+
action: 'updateUser',
54+
user: {
55+
name: profile.name,
56+
},
57+
};
58+
};
59+
```
60+
61+
Actions run in the authentication request path. Keep external services fast and highly available, and assume that a user may retry the sign-in. Logto does not retry an Action or fall back from the Logto Cloud remote runner to local execution.
62+
63+
### Use environment variables \{#use-environment-variables}
64+
65+
Use environment variables for values that should not be hardcoded in the script, such as API URLs, tokens, and feature settings:
66+
67+
```js
68+
const { API_URL, API_TOKEN } = environmentVariables;
69+
```
70+
71+
Environment variables are part of the Action configuration and are visible to administrators who can read that configuration. Restrict Action-management access and never include secrets in the returned result or an error message.
72+
73+
## Supported user patch \{#supported-user-patch}
74+
75+
An Action can return only these user fields:
76+
77+
| Field | Description |
78+
| -------------- | ----------------------------------------- |
79+
| `username` | Username |
80+
| `primaryEmail` | Primary email address |
81+
| `primaryPhone` | Primary phone number |
82+
| `name` | Display name |
83+
| `avatar` | Avatar URL |
84+
| `profile` | Standard OIDC profile fields |
85+
| `customData` | Additional JSON data for your application |
86+
87+
The corresponding patch type is:
88+
89+
```ts
90+
type ActionUserPatch = {
91+
username?: string | null;
92+
primaryEmail?: string | null;
93+
primaryPhone?: string | null;
94+
name?: string | null;
95+
avatar?: string | null;
96+
customData?: Record<string, JsonValue>;
97+
profile?: {
98+
familyName?: string;
99+
givenName?: string;
100+
middleName?: string;
101+
nickname?: string;
102+
preferredUsername?: string;
103+
profile?: string;
104+
website?: string;
105+
gender?: string;
106+
birthdate?: string;
107+
zoneinfo?: string;
108+
locale?: string;
109+
address?: {
110+
formatted?: string;
111+
streetAddress?: string;
112+
locality?: string;
113+
region?: string;
114+
postalCode?: string;
115+
country?: string;
116+
};
117+
};
118+
};
119+
```
120+
121+
Fields such as user ID, suspension state, identities, roles, organizations, MFA configuration, password hashes, and other internal fields are rejected.
122+
123+
For updates, `profile` and `customData` are shallow-merged with the existing objects. Returning a nested object with an existing top-level key replaces the value at that key; it is not a deep merge. Identifier updates must also pass Logto's uniqueness checks.
124+
125+
## Test context and dry runs \{#test-context-and-dry-runs}
126+
127+
The **Test context** is sample JSON used only when you click **Run test**. It is saved with the Action for future tests, but production executions always use the real authentication event.
128+
129+
A dry run:
130+
131+
- Uses the current unsaved script, sample event, and environment variables.
132+
- Executes the script and displays its raw return value.
133+
- Does not save the Action or create or update a Logto user.
134+
- Does not emit a production Action audit event or execution metric.
135+
- Does not apply the production event and result validation for the selected action type.
136+
137+
:::caution
138+
A successful dry run proves that the script executed, but not that its result will be accepted during a real authentication flow. Test the complete flow in a non-production tenant before enabling the Action in production.
139+
140+
Successful test results are displayed as returned. Never return passwords, environment variables, API tokens, or other secrets from a script.
141+
:::
142+
143+
## Handle errors \{#handle-errors}
144+
145+
The **On script error** setting applies to execution failures such as a thrown exception, a rejected promise, a failed external request, or a runner failure. It does not make an invalid result valid.
146+
147+
| Action type | `block` (default) | `allow` |
148+
| ------------------------------ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
149+
| Post first-factor verification | Reject the invalid local credentials. | Not available in Console. Even if set through the API, an execution failure still rejects the credentials. |
150+
| Post sign-in | Fail the sign-in. | Continue the sign-in without applying the Action update. |
151+
152+
For Post sign-in, a malformed or unsupported return value always fails the sign-in, even when `allow` is selected. Validate every success path in your script and return an explicit supported result or no-op.
153+
154+
## Monitor executions \{#monitor-executions}
155+
156+
Production executions create independent [audit log](/developers/audit-logs) events:
157+
158+
- `Action.PostFirstFactorVerification`
159+
- `Action.PostSignIn`
160+
161+
The audit record includes safe execution metadata such as the Action type, runtime location, duration, decision, and error-policy outcome. Passwords, environment-variable values, script source, and other sensitive values are redacted.
162+
163+
## Configure Actions with the Management API \{#configure-actions-with-the-management-api}
164+
165+
You can also manage Actions through the [Logto Management API](/integrate-logto/interact-with-management-api):
166+
167+
| Method | Endpoint | Purpose |
168+
| -------- | ----------------------------------- | ------------------------------------ |
169+
| `GET` | `/api/configs/actions` | List configured Actions |
170+
| `GET` | `/api/configs/actions/{actionType}` | Get one Action |
171+
| `PUT` | `/api/configs/actions/{actionType}` | Create or replace an Action |
172+
| `PATCH` | `/api/configs/actions/{actionType}` | Partially update an Action |
173+
| `DELETE` | `/api/configs/actions/{actionType}` | Delete an Action |
174+
| `POST` | `/api/configs/actions/test` | Dry-run a script with a sample event |
175+
176+
The action type values retain their original identifiers for backward compatibility:
177+
178+
- `inlineHook.postFirstFactorVerification`
179+
- `inlineHook.postSignIn`
180+
181+
An Action configuration has this shape:
182+
183+
```ts
184+
type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
185+
186+
type ActionConfig = {
187+
script: string;
188+
environmentVariables?: Record<string, string>;
189+
contextSample?: JsonValue;
190+
enabled?: boolean;
191+
onExecutionError?: 'block' | 'allow';
192+
};
193+
```
194+
195+
When using the API, set `enabled: true` explicitly to run the Action. If `onExecutionError` is omitted, it defaults to `block`.

0 commit comments

Comments
 (0)