@lowdefy/docs-content 0.0.0-experimental-20260928120643 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +0 -1
  2. package/content/actions/link.md +0 -17
  3. package/content/actions/login.md +5 -23
  4. package/content/actions/logout.md +5 -5
  5. package/content/actions/pdfmake.md +2 -2
  6. package/content/actions/setdarkmode.md +2 -2
  7. package/content/agents/agent-properties.md +2 -4
  8. package/content/agents/agentchat.md +6 -21
  9. package/content/agents/agentconversations.md +2 -2
  10. package/content/concepts/ai-agent-docs.md +101 -313
  11. package/content/concepts/cli.md +3 -34
  12. package/content/concepts/connections-and-requests.md +0 -402
  13. package/content/concepts/custom-html.md +0 -40
  14. package/content/concepts/custom-styling.md +5 -50
  15. package/content/concepts/events-and-actions.md +2 -64
  16. package/content/concepts/i18n.md +2 -7
  17. package/content/concepts/keyboard-shortcuts.md +2 -2
  18. package/content/concepts/layout-overview.md +1 -1
  19. package/content/concepts/logger.md +0 -81
  20. package/content/concepts/lowdefy-schema.md +1 -4
  21. package/content/concepts/menus.md +7 -7
  22. package/content/concepts/module-authoring.md +2 -2
  23. package/content/connections/anthropic.md +2 -162
  24. package/content/connections/awss3.md +0 -54
  25. package/content/connections/google.md +2 -162
  26. package/content/connections/mongodb.md +3 -121
  27. package/content/connections/openai-connection.md +2 -162
  28. package/content/container-blocks/affix.md +7 -47
  29. package/content/container-blocks/alert.md +24 -76
  30. package/content/container-blocks/badge.md +24 -100
  31. package/content/container-blocks/box.md +9 -9
  32. package/content/container-blocks/card.md +25 -80
  33. package/content/container-blocks/carousel.md +7 -60
  34. package/content/container-blocks/collapse.md +9 -87
  35. package/content/container-blocks/configprovider.md +13 -35
  36. package/content/container-blocks/confirmmodal.md +23 -69
  37. package/content/container-blocks/content.md +98 -62
  38. package/content/container-blocks/descriptions.md +4 -45
  39. package/content/container-blocks/drawer.md +28 -92
  40. package/content/container-blocks/dropdownmenu.md +31 -119
  41. package/content/container-blocks/flex.md +62 -40
  42. package/content/container-blocks/footer.md +11 -15
  43. package/content/container-blocks/googlemapsheatmap.md +1 -1
  44. package/content/container-blocks/googlemapsscript.md +2 -2
  45. package/content/container-blocks/header.md +36 -71
  46. package/content/container-blocks/label.md +7 -34
  47. package/content/container-blocks/layout.md +34 -52
  48. package/content/container-blocks/masonry.md +71 -17
  49. package/content/container-blocks/mobilemenu.md +88 -131
  50. package/content/container-blocks/modal.md +26 -114
  51. package/content/container-blocks/pageheadermenu.md +31 -81
  52. package/content/container-blocks/pagesidebarlayout.md +26 -169
  53. package/content/container-blocks/pagesidermenu.md +42 -95
  54. package/content/container-blocks/popover.md +31 -34
  55. package/content/container-blocks/result.md +21 -32
  56. package/content/container-blocks/sider.md +122 -85
  57. package/content/container-blocks/span.md +7 -7
  58. package/content/container-blocks/spinner.md +3 -3
  59. package/content/container-blocks/splitter.md +68 -150
  60. package/content/container-blocks/tabs.md +12 -64
  61. package/content/container-blocks/tooltip.md +46 -22
  62. package/content/container-blocks/watermark.md +4 -22
  63. package/content/controls/reject.md +2 -4
  64. package/content/controls/throw.md +0 -1
  65. package/content/controls/try.md +2 -3
  66. package/content/deployment/deployment-environments.md +6 -8
  67. package/content/deployment/docker.md +7 -22
  68. package/content/deployment/node-server.md +0 -4
  69. package/content/deployment/vercel.md +6 -13
  70. package/content/display-blocks/aggrid.md +11 -26
  71. package/content/display-blocks/anchor.md +15 -15
  72. package/content/display-blocks/avatar.md +37 -57
  73. package/content/display-blocks/breadcrumb.md +21 -70
  74. package/content/display-blocks/button.md +18 -92
  75. package/content/display-blocks/calendar.md +0 -12
  76. package/content/display-blocks/dangeroushtml.md +1 -1
  77. package/content/display-blocks/difflist.md +1 -1
  78. package/content/display-blocks/difftimeline.md +1 -1
  79. package/content/display-blocks/divider.md +3 -64
  80. package/content/display-blocks/dropdownbutton.md +28 -79
  81. package/content/display-blocks/echart.md +1 -35
  82. package/content/display-blocks/floatbutton.md +42 -129
  83. package/content/display-blocks/html.md +1 -61
  84. package/content/display-blocks/icon.md +67 -250
  85. package/content/display-blocks/menu.md +73 -145
  86. package/content/display-blocks/message.md +124 -38
  87. package/content/display-blocks/notification.md +222 -94
  88. package/content/display-blocks/paragraph.md +9 -59
  89. package/content/display-blocks/progress.md +17 -96
  90. package/content/display-blocks/qrcode.md +4 -19
  91. package/content/display-blocks/s3download.md +1 -1
  92. package/content/display-blocks/search.md +7 -17
  93. package/content/display-blocks/skeletonbutton.md +0 -1
  94. package/content/display-blocks/skeletoninput.md +0 -1
  95. package/content/display-blocks/statistic.md +17 -75
  96. package/content/display-blocks/steps.md +25 -58
  97. package/content/display-blocks/tag.md +17 -102
  98. package/content/display-blocks/title.md +11 -50
  99. package/content/display-blocks/tour.md +23 -33
  100. package/content/input-blocks/autocomplete.md +9 -81
  101. package/content/input-blocks/buttonselector.md +8 -45
  102. package/content/input-blocks/checkboxselector.md +7 -97
  103. package/content/input-blocks/checkboxswitch.md +3 -33
  104. package/content/input-blocks/colorselector.md +14 -63
  105. package/content/input-blocks/daterangeselector.md +45 -165
  106. package/content/input-blocks/dateselector.md +41 -139
  107. package/content/input-blocks/datetimeselector.md +49 -158
  108. package/content/input-blocks/inputgallery.md +3 -3
  109. package/content/input-blocks/monthselector.md +52 -120
  110. package/content/input-blocks/multipleselector.md +39 -150
  111. package/content/input-blocks/numberinput.md +12 -98
  112. package/content/input-blocks/pagination.md +14 -70
  113. package/content/input-blocks/paragraphinput.md +37 -51
  114. package/content/input-blocks/passwordinput.md +6 -60
  115. package/content/input-blocks/phonenumberinput.md +26 -59
  116. package/content/input-blocks/radioselector.md +7 -83
  117. package/content/input-blocks/ratingslider.md +26 -57
  118. package/content/input-blocks/s3uploadbutton.md +24 -24
  119. package/content/input-blocks/s3uploaddragger.md +3 -3
  120. package/content/input-blocks/s3uploadphoto.md +5 -5
  121. package/content/input-blocks/segmentedselector.md +32 -72
  122. package/content/input-blocks/selector.md +21 -109
  123. package/content/input-blocks/switch.md +17 -65
  124. package/content/input-blocks/textarea.md +3 -32
  125. package/content/input-blocks/textinput.md +28 -69
  126. package/content/input-blocks/tiptapmentioninput.md +1 -1
  127. package/content/input-blocks/titleinput.md +12 -39
  128. package/content/input-blocks/treeinput.md +25 -49
  129. package/content/input-blocks/treemultipleselector.md +10 -127
  130. package/content/input-blocks/treeselector.md +9 -97
  131. package/content/input-blocks/upload.md +15 -64
  132. package/content/input-blocks/uploaddragger.md +1 -17
  133. package/content/input-blocks/uploadphoto.md +1 -18
  134. package/content/input-blocks/weekselector.md +55 -120
  135. package/content/list-blocks/controlledlist.md +15 -24
  136. package/content/list-blocks/list.md +11 -11
  137. package/content/list-blocks/masonrylist.md +34 -7
  138. package/content/migration/upgrade-guide.md +0 -1
  139. package/content/operators/_app.md +1 -2
  140. package/content/operators/_error.md +1 -23
  141. package/content/operators/_get.md +6 -6
  142. package/content/operators/_operator.md +3 -17
  143. package/content/operators/_ref.md +0 -258
  144. package/content/operators/_type.md +3 -14
  145. package/content/operators/_user.md +3 -3
  146. package/content/other/introduction.md +1 -1
  147. package/content/plugins/plugins-dev.md +0 -66
  148. package/content/plugins/plugins-introduction.md +1 -27
  149. package/content/testing/e2e-configuration.md +1 -8
  150. package/content/testing/e2e-getting-started.md +1 -1
  151. package/content/testing/e2e-introduction.md +1 -1
  152. package/content/tutorial/tutorial-actions-operators.md +2 -2
  153. package/content/tutorial/tutorial-add-blocks.md +2 -2
  154. package/content/tutorial/tutorial-create-page.md +2 -48
  155. package/content/tutorial/tutorial-display-data-page.md +3 -3
  156. package/content/tutorial/tutorial-requests-sql.md +0 -153
  157. package/content/tutorial/tutorial-start.md +1 -66
  158. package/content/user-authentication/login-and-logout.md +8 -158
  159. package/content/user-authentication/users-introduction.md +0 -8
  160. package/index.json +2 -6065
  161. package/package.json +1 -7
  162. package/scripts/extractAgentDocs.js +4 -36
  163. package/content/actions/emailotpsend.md +0 -58
  164. package/content/actions/emailotpverify.md +0 -77
  165. package/content/actions/getlocalstorage.md +0 -41
  166. package/content/actions/magiclinkverify.md +0 -113
  167. package/content/actions/posthogalias.md +0 -29
  168. package/content/actions/posthogcapture.md +0 -49
  169. package/content/actions/posthogcapturepageleave.md +0 -38
  170. package/content/actions/posthogfeatureflag.md +0 -50
  171. package/content/actions/posthoggroup.md +0 -37
  172. package/content/actions/posthogidentify.md +0 -54
  173. package/content/actions/posthoginit.md +0 -66
  174. package/content/actions/posthogoptin.md +0 -29
  175. package/content/actions/posthogoptout.md +0 -29
  176. package/content/actions/posthogpageview.md +0 -37
  177. package/content/actions/posthogreloadfeatureflags.md +0 -47
  178. package/content/actions/posthogreset.md +0 -28
  179. package/content/actions/posthogsetpersonproperties.md +0 -36
  180. package/content/actions/removelocalstorage.md +0 -26
  181. package/content/actions/setlocalstorage.md +0 -32
  182. package/content/adapter-reference/mongodbauthadapter.md +0 -46
  183. package/content/agents/agents-introduction.md +0 -107
  184. package/content/callback-reference/auth0logoutcallback.md +0 -54
  185. package/content/concepts/blocks.md +0 -150
  186. package/content/concepts/dynamic-page-content.md +0 -304
  187. package/content/concepts/html-attributes.md +0 -285
  188. package/content/concepts/lowdefy-api.md +0 -1110
  189. package/content/concepts/notifications.md +0 -155
  190. package/content/concepts/page-and-app-state.md +0 -244
  191. package/content/concepts/theming.md +0 -582
  192. package/content/connections/aigateway.md +0 -368
  193. package/content/connections/axioshttp.md +0 -181
  194. package/content/controls/while.md +0 -46
  195. package/content/display-blocks/clickablehtml.md +0 -239
  196. package/content/display-blocks/eventlog.md +0 -363
  197. package/content/display-blocks/pollingtimer.md +0 -263
  198. package/content/input-blocks/captcha.md +0 -40
  199. package/content/input-blocks/placesautocomplete.md +0 -319
  200. package/content/input-blocks/tagmultipleselector.md +0 -114
  201. package/content/input-blocks/tagselector.md +0 -114
  202. package/content/migration/auth-upgrade.md +0 -327
  203. package/content/migration/v3-to-v4.md +0 -417
  204. package/content/migration/v6-to-v7.md +0 -222
  205. package/content/operators/_cron.md +0 -292
  206. package/content/other/mongodbadapter.md +0 -3
  207. package/content/plugins/plugins-icon-sets.md +0 -267
  208. package/content/plugins/posthog.md +0 -275
  209. package/content/provider-reference/auth-js-providers.md +0 -107
  210. package/content/provider-reference/openidconnectprovider.md +0 -44
  211. package/content/testing/config-tests.md +0 -278
  212. package/content/tutorial/tutorial-requests-api.md +0 -155
  213. package/content/user-authentication/auth-configuration.md +0 -266
  214. package/content/user-authentication/auth-steps.md +0 -138
  215. package/content/user-authentication/mcp-oauth.md +0 -292
  216. package/content/user-authentication/organizations.md +0 -221
  217. package/content/user-authentication/protected-pages-apis.md +0 -97
  218. package/content/user-authentication/roles.md +0 -49
  219. package/content/user-authentication/two-factor.md +0 -229
  220. package/content/user-authentication/user-object.md +0 -58
  221. package/content/websockets/websocket-publish.md +0 -108
  222. package/content/websockets/websocket-subscriptions.md +0 -100
  223. package/content/websockets/websockets-introduction.md +0 -97
  224. package/hazards.json +0 -44
  225. package/scripts/hashDocSources.js +0 -38
  226. package/scripts/recordSourceHashes.js +0 -88
@@ -1,266 +0,0 @@
1
- # Auth Configuration
2
-
3
- The `auth` section configures user authentication. Lowdefy's auth is built on [BetterAuth](https://www.better-auth.com/): sessions are database-backed, the app owns its auth UI, and roles live on organization memberships. If you are moving from the previous NextAuth-shaped config, start with the [Auth Upgrade guide](/auth-upgrade) — several keys were renamed or removed.
4
-
5
- The `_secret` operator is evaluated over the entire `auth` section, so any value can be a secret reference.
6
-
7
- For a login method to work you must configure **at least one mechanism** (`emailAndPassword`, `magicLink`, `emailOTP`, `phoneNumber`, an OAuth `providers` entry, or an API `strategies` entry), a `secret`, and a `database`. An `auth` block with none of these fails the build.
8
-
9
- ## Secret
10
-
11
- A required signing secret, given as a `_secret` reference:
12
-
13
- ```yaml
14
- lowdefy: 6.0.0
15
- auth:
16
- secret:
17
- _secret: BETTER_AUTH_SECRET
18
- ```
19
-
20
- Also pin the app's canonical origin — the `url` of the current environment in [`config.environments`](/lowdefy-schema), or the `BETTER_AUTH_URL` environment variable, which wins — auth builds password-reset, magic-link and verification links, and its CSRF origin allowlist, from it. With `auth.email` configured, a production server refuses to start without it. See the [migration guide](/auth-upgrade#3-pin-the-canonical-url-with-better-auth-url).
21
-
22
- ## Database
23
-
24
- Sessions, users, accounts and organizations are stored through an adapter:
25
-
26
- ```yaml
27
- lowdefy: 6.0.0
28
- auth:
29
- database:
30
- id: auth_db
31
- type: MongoDBAuthAdapter
32
- properties:
33
- uri:
34
- _secret: AUTH_DATABASE_URI
35
- ```
36
-
37
- See the [MongoDBAuthAdapter](/MongoDBAuthAdapter) reference. Collection names follow the `user-*` convention and are fixed by the adapter.
38
-
39
- ## Login mechanisms
40
-
41
- - **Email & password** — `auth.emailAndPassword` (`enabled`, `requireEmailVerification`, `minPasswordLength` (default 8), `disableSignUp`).
42
- - **Magic link** — `auth.magicLink` (`enabled`, `expiresIn` seconds (default 300), `disableSignUp`), which needs `auth.email` (below).
43
- - **Email one-time code** — `auth.emailOTP` (`enabled`, `otpLength` (default 6), `expiresIn` seconds (default 300), `allowedAttempts` (default 3), `disableSignUp` (default `false`)), which needs `auth.email` (below). The person is emailed a code and types it into the tab they started from, so the sign-in survives mail security that pre-fetches links, and works when the email is opened on another device.
44
- - **OAuth providers** — `auth.providers`; see [Providers](/auth-providers).
45
- - **Phone number** — `auth.phoneNumber` (OTP sign-in); needs a `phone.otp.send` hook to deliver the SMS.
46
- - **Passkeys** — `auth.passkey` (`enabled`, `rpId`, `rpName`).
47
-
48
- ```yaml
49
- lowdefy: 6.0.0
50
- auth:
51
- emailAndPassword:
52
- enabled: true
53
- requireEmailVerification: true
54
- ```
55
-
56
- ## Auth email
57
-
58
- `auth.email` references an **SMTP connection** by id — the connection owns `from`, `replyTo`, the transport and the delivery filter, and is shared by every auth email flow. There is no inline transport shape. Optionally map individual flows to your own notification templates:
59
-
60
- ```yaml
61
- lowdefy: 6.0.0
62
- auth:
63
- email:
64
- connectionId: email # an SMTP connection in connections[]
65
- templates: # all optional; unset → branded stock template
66
- verifyEmail: verify-email-notification
67
- resetPassword: reset-password-notification
68
- magicLink: magic-link-notification
69
- invitation: invite-notification
70
- emailOTP: email-otp-notification
71
- ```
72
-
73
- ### Link and code in one email
74
-
75
- With both `magicLink` and `emailOTP` enabled, the magic-link email also carries the one-time code, so a single email serves both paths: the person clicks the button, or types the code into the tab they asked for the link from. Nothing extra is sent — the `emailOTP` template is only used for a code-only send.
76
-
77
- The `magicLink` template then has two more variables available: `otp` (the code) and `expiresIn` (the code's lifetime in seconds, from `auth.emailOTP.expiresIn`). The stock template renders them below the sign-in button; a template of your own can place them wherever it likes. With `emailOTP` off both are absent and the link email is unchanged.
78
-
79
- ## Auth pages
80
-
81
- BetterAuth ships no UI — your app owns its auth pages, and `auth.authPages` points at them. All are Lowdefy page paths:
82
-
83
- | Key | Default | When required |
84
- | --- | ------- | ------------- |
85
- | `signIn` | `/login` | |
86
- | `signUp` | `/signup` | |
87
- | `error` | `/auth/error` | receives `?error=` code |
88
- | `forgotPassword` | `/forgot-password` | |
89
- | `resetPassword` | `/reset-password` | where the emailed reset link lands, with `?token=` (or `?error=INVALID_TOKEN`); the default `redirectTo` of `RequestPasswordReset` |
90
- | `verifyEmail` | `/verify-email` | where the emailed verification link lands; receives `?error=` code when the link is invalid or expired |
91
- | `twoFactor` | — | **required** when `twoFactor.enabled` |
92
- | `twoFactorEnrol` | — | **required** when `twoFactor.required` |
93
- | `acceptInvitation` | — | consumes an `?invitationId=` link |
94
- | `magicLink` | — | landing page for the sign-in email (opt-in; see below) |
95
-
96
- ```yaml
97
- lowdefy: 6.0.0
98
- auth:
99
- authPages:
100
- signIn: /login
101
- signUp: /signup
102
- error: /auth-error
103
- ```
104
-
105
- ### The email verification page
106
-
107
- A verification email links to Better Auth's verify endpoint, which confirms the address and then redirects to `authPages.verifyEmail`. When the link is invalid or has expired, it redirects there with `?error=` set (`INVALID_TOKEN`, `TOKEN_EXPIRED`), so the page can offer to resend with the `SendVerificationEmail` action. A `callbackUrl` on the `SignUp` or `SendVerificationEmail` action sends that link somewhere else instead.
108
-
109
- ### The magic link landing page
110
-
111
- `authPages.magicLink` is opt-in, and it exists for one reason: Better Auth's magic-link verify endpoint consumes the single-use token on the **first** `GET` of the URL. Corporate mail security — Microsoft Defender Safe Links, Proofpoint URL Defense, Mimecast — fetches every link in a message at delivery time, so that first `GET` is a scanner and the person clicking arrives to `INVALID_TOKEN`.
112
-
113
- Set the key and the sign-in email links to a page of your own instead, with the token and the callback destinations carried on its URL query. Fetching a Lowdefy page is harmless, so the token stays unspent until a real click runs [`MagicLinkVerify`](/MagicLinkVerify):
114
-
115
- ```yaml
116
- lowdefy: 6.0.0
117
- auth:
118
- magicLink:
119
- enabled: true
120
- authPages:
121
- magicLink: /magic-link
122
- pages:
123
- public:
124
- - magic-link
125
- ```
126
-
127
- ```yaml
128
- id: magic-link
129
- type: PageHeaderMenu
130
- blocks:
131
- - id: content
132
- type: Box
133
- blocks:
134
- - id: title
135
- type: Title
136
- properties:
137
- content: Sign in
138
- level: 3
139
- - id: description
140
- type: Paragraph
141
- properties:
142
- content: Click the button below to finish signing in.
143
- - id: verify_button
144
- type: Button
145
- properties:
146
- title: Sign in
147
- events:
148
- onClick:
149
- - id: verify
150
- type: MagicLinkVerify
151
- ```
152
-
153
- The page must **not** call `MagicLinkVerify` in `onMount`, and must not auto-redirect to the verify endpoint. Scanning sandboxes that render the page and execute its JavaScript would consume the token anyway, which puts you back where you started. The protection is the click, not the page.
154
-
155
- Leave the key unset and magic-link sign-in keeps working exactly as before, linking straight at the verify endpoint.
156
-
157
- See [Two-Factor Authentication](/two-factor) for the two-factor pages, and the [MCP Server & OAuth](/mcp-oauth) page for `oauthProvider.consentPage` and `postLoginPage`.
158
-
159
- ## Session
160
-
161
- Database sessions. Length is `expiresIn` (seconds, default 604800 = 7 days); `updateAge` (default 86400) is how often an active session's expiry is refreshed:
162
-
163
- ```yaml
164
- lowdefy: 6.0.0
165
- auth:
166
- session:
167
- expiresIn: 43200 # 12 hours in seconds
168
- updateAge: 3600
169
- ```
170
-
171
- `session.cookieCache` (off by default) trades a short window of stale session reads for fewer database lookups — leave it off unless you understand the [revocation-latency trade](/two-factor#recovering-a-user-who-has-lost-their-factor).
172
-
173
- ## Roles, organizations, and API strategies
174
-
175
- - **Roles** — declare app role names in `auth.roles` and gate pages/endpoints with `auth.pages.roles` / `auth.api.roles`. See [Roles](/roles).
176
- - **Organizations** — `auth.organizations.policy` (`pinned` or `tenant`) decides the whole multi-tenancy model. See [Organizations & Multi-Tenancy](/organizations).
177
- - **API strategies** — `auth.strategies` authenticates non-session callers (server-to-server) with an `apiKey` or a `jwt`:
178
-
179
- ```yaml
180
- lowdefy: 6.0.0
181
- auth:
182
- strategies:
183
- - id: service-key
184
- type: apiKey
185
- roles:
186
- - integrations
187
- properties:
188
- keys:
189
- - value:
190
- _secret: SERVICE_API_KEY
191
- ```
192
-
193
- A strategy caller carries the configured `roles` and `attributes` and its `auth_method` is the strategy type. A `jwt` strategy needs exactly one of `properties.secret` or `properties.jwksUri`, and a non-empty `algorithms` allowlist (which blocks `alg: none` downgrades).
194
-
195
- ## Captcha
196
-
197
- `auth.captcha` protects auth endpoints with Cloudflare Turnstile. The `siteKey` is public (a plain string, never a `_secret`); the `secretKey` is a `_secret` reference. See the [Captcha block](/Captcha) for the client side.
198
-
199
- ## Rate limiting and account linking
200
-
201
- - `auth.rateLimit` — brute-force protection, on by default (`enabled`, `window` seconds, `max`). Sign-in and sign-up allow 3 attempts per 10 seconds, and password-reset and verification emails 3 per minute. Limits count per client address: the connection's address on a self-hosted server, or the address a trusted proxy forwarded when the app lists its proxies in [`config.trustedProxies`](/docker#behind-a-reverse-proxy). `X-Forwarded-For` from anyone else is ignored.
202
- - `auth.account.accountLinking` — link a new OAuth sign-in to an existing account by verified email. `trustedProviders` lists providers whose email claim you trust for linking. **This is unrelated to `provider.twoFactorTrusted`** — see [Two-Factor Authentication](/two-factor#trusting-an-oauth-provider).
203
-
204
- ## Mock User for Testing (Dev Server Only)
205
-
206
- When developing and testing Lowdefy apps, you can bypass the login flow by configuring a mock user. This is useful for testing authenticated flows without going through OAuth login.
207
-
208
- The mock user can be configured in three ways:
209
-
210
- ### CLI Flag
211
-
212
- Pass `--mock-user` to `lowdefy dev` to start the server as a mock user for that run. Supply a JSON user object to set the identity and roles, or use the bare flag for a default user with no roles:
213
-
214
- ```bash
215
- lowdefy dev --mock-user '{"id":"test-user","email":"test@example.com","roles":["admin"]}'
216
- ```
217
-
218
- The flag sets `LOWDEFY_DEV_USER` for the dev server process, so it takes precedence over `auth.dev.mockUser` in the config file.
219
-
220
- ### Environment Variable
221
-
222
- Set the `LOWDEFY_DEV_USER` environment variable to a JSON string containing the mock user object:
223
-
224
- ```bash
225
- LOWDEFY_DEV_USER='{"id":"test-user","email":"test@example.com","roles":["admin"]}'
226
- ```
227
-
228
- ### Config File
229
-
230
- Add the `auth.dev.mockUser` section to your `lowdefy.yaml`:
231
-
232
- ###### Configure a mock user for development
233
- ```yaml
234
- lowdefy: 6.0.0
235
-
236
- auth:
237
- providers:
238
- - id: google
239
- type: Google
240
- properties:
241
- clientId:
242
- _secret: GOOGLE_CLIENT_ID
243
- clientSecret:
244
- _secret: GOOGLE_CLIENT_SECRET
245
- dev:
246
- mockUser:
247
- id: test-user
248
- email: test@example.com
249
- name: Test User
250
- roles:
251
- - admin
252
- ```
253
-
254
- When a mock user is configured:
255
- - The environment variable takes precedence over the config file if both are set
256
- - A warning is logged at dev server startup: "Mock user active - login bypassed"
257
- - The mock user is injected as a pre-resolved caller — its `roles` are authoritative
258
- - The `_user` operator returns values from the mock user, on the server and in the browser client — the dev server serves the mock session to both
259
- - Protected pages are accessible based on the mock user's roles
260
- - The dev server's headless renderer (used by the AI-agent screenshot and state-inspection tools) renders as the mock user, so it can capture pages with roles the default user lacks
261
-
262
- > **Note:** Mock users only work with the development server (`lowdefy dev`). The production server ignores mock user configuration for security.
263
-
264
- > **Note:** `dev.mockUser` bypasses the auth engine rather than exercising it, so [auth steps](/auth-steps) (which need the running auth engine) are unavailable under a mock user.
265
-
266
- `auth.dev` needs no auth stack behind it. An `auth` block whose only key is `dev` is not an auth configuration: it demands no login method, no `secret` and no `database`, the app builds and runs signed out in production, and in the dev server the browser is the `dev.mockUser` caller. Adding any other `auth` key declares a real auth stack, and the build then requires a mechanism, a secret and (for a login method) a database as usual.
@@ -1,138 +0,0 @@
1
- # Auth Steps
2
-
3
- Auth steps are routine steps that administer users, organizations and memberships — inviting a member, updating an app role, banning a user, creating an organization. They run **inside an `Api` or `InternalApi` endpoint routine**, not as client actions, because administering *other* people's memberships is a server operation that must be authorized against the caller, name its target organization explicitly, and be visible in a diff.
4
-
5
- > A **client action** mutates the caller's *own* session and nothing else — `SetActiveOrganization`, `AcceptInvitation`, `LeaveOrganization`. Anything that reaches another person's membership is an auth **step** in a routine, which is what earns it the per-step authority floor below. See the [Auth Upgrade guide](/auth-upgrade#retired-client-actions) for the actions that became steps.
6
-
7
- ```yaml
8
- id: invite-member
9
- type: Api
10
- routine:
11
- - id: invite
12
- type: InviteMember
13
- properties:
14
- organizationId:
15
- _user: organization_id
16
- email:
17
- _payload: email
18
- appRoles:
19
- _payload: roles
20
- - ':return':
21
- _step: invite
22
- ```
23
-
24
- ## The authority model
25
-
26
- Every auth step declares, in its own code, the authority it requires — and the engine's **authority floor** enforces that declaration mechanically before the step runs. This is not optional decoration: a step with no declared authority is a build/runtime error, so an app can never expose a member mutation by forgetting a check. The reason the floor exists in one place is that most steps go adapter-direct or through BetterAuth's admin plugin, whose check reads a single deployment-wide `user.role` field and **cannot** answer "is the caller an administrator of *this* organization". For those steps, the floor is the only authorization that can express per-organization authority.
27
-
28
- A step declares one of three **scopes**.
29
-
30
- ### `org` — authorized against the caller's membership in the target organization
31
-
32
- The common case. The floor:
33
-
34
- 1. **Resolves the target organization** (see below).
35
- 2. **Finds the caller's `member` row** in that organization and checks its role against the step's required **permissions**. The caller's authority is their membership in the *target* organization — an administrator of the team organization holds nothing in the customer organization.
36
- 3. For steps that write the deployment-wide `user` row, additionally requires the **target user** to be a member of that organization (`targetUser`), so org authority alone can never reach a user who is not the caller's business.
37
-
38
- Permissions map to the organization tier via BetterAuth's organization access control: `owner` and `admin` are granted the member-management permissions out of the box; `member` holds none of them. A refused caller gets a `ConfigError` naming exactly what was required.
39
-
40
- ### `system` — caller-less, trusted runs only
41
-
42
- A `system`-scoped step (`CreateOrganization`, `ListUsers`) has no organization to be authorized in, so a caller can never satisfy it. It runs **only** where the run itself is trusted — a caller-less system routine. Mark the step `system: true`, or run it from a system context (cron, a verified webhook, an auth hook):
43
-
44
- ```yaml
45
- - id: create_org
46
- type: CreateOrganization
47
- system: true
48
- properties:
49
- name:
50
- _payload: name
51
- slug:
52
- _payload: slug
53
- userId:
54
- _payload: ownerUserId
55
- ```
56
-
57
- ### `caller` — acts on the caller's own records
58
-
59
- A `caller`-scoped step (`RevokeMcpGrant`, `LeaveOrganization`) acts only on rows the caller owns, so it needs no organization authority — but it is meaningless without a caller, and the system has no rows of its own. It requires a real caller and refuses `system: true`.
60
-
61
- ### Leaving an organization from a routine
62
-
63
- `LeaveOrganization` ends the caller's own membership of the organization named by `organizationId`. The property is required: the step has no org scope, so nothing defaults it. The organization plugin's own checks run, so a caller who is not a member there, or who is its only owner, is refused. The refusal throws with the plugin's message, which the routine catches with `:try` to branch on:
64
-
65
- ```yaml
66
- id: leave
67
- type: Api
68
- routine:
69
- - :try:
70
- - id: leave
71
- type: LeaveOrganization
72
- properties:
73
- organizationId:
74
- _payload: organization_id
75
- :catch:
76
- - :return:
77
- refused:
78
- message:
79
- _error: message
80
- # Runs only when the leave went through.
81
- - :return:
82
- success: true
83
- ```
84
-
85
- On success the step returns the member row it removed. The caller's session still names the organization they left as active, so their next request resolves unauthenticated: follow the call on the page with `SetActiveOrganization` for another organization, or with `Logout`. The `LeaveOrganization` client action leaves the session's active organization from the browser and clears it; use it when nothing else has to happen in the same request as the leave. Step and action types resolve from separate maps, so the shared name is never ambiguous.
86
-
87
- ## `system: true` — the two doors to a caller-less run
88
-
89
- The floor's caller requirement passes on either of two independent doors:
90
-
91
- - a **run-level** system context (`context.system === true`) — a trusted, caller-less run such as a cron endpoint, an auth hook, or a verified webhook; or
92
- - a **per-step** `system: true` on a single step in an otherwise user-driven run — a waiver visible in the diff, for the one privileged operation in a member-facing routine.
93
-
94
- Either door makes the step act caller-less **as the system**, bypassing the org/permission checks. That is an explicit trust decision, not attribution — use it deliberately.
95
-
96
- ## How the target organization is resolved
97
-
98
- For every `org`-scoped step the floor resolves the target organization once, and the step writes into the *same* one it authorized — they cannot drift. The rule:
99
-
100
- 1. An explicit **`organizationId`** property always wins. This is what a multi-organization admin routine passes, and it is what lets a session pinned to the team organization administer the customer organization.
101
- 2. `ListMembers` also accepts an **`organizationSlug`**, resolved to an id here.
102
- 3. Otherwise the **pinned** organization is the default — **but under `policy: tenant` there is no pinned organization**, so an omitted `organizationId` is a runtime error naming the fix. Under `tenant`, always pass `organizationId` explicitly.
103
-
104
- ## `targetUser` and `selfTargetExempt`
105
-
106
- Two refinements the floor reads from a step's declaration:
107
-
108
- - **`targetUser`** names the property holding the id of the user being acted on. Steps that write the shared `user` row declare it, so the floor can require that user to be a member of the target organization. An absent target-user property is the step's own required-property error, not the floor's.
109
- - **`selfTargetExempt`** lets a caller act on **their own** row without holding org authority — this is what makes `UpdateUserProfile` a self-service "save my profile" step as well as an admin one. When the named property equals the caller's own id, the org authority check is skipped.
110
-
111
- ## Step reference
112
-
113
- Scope, required permission, and target for every auth step. Permissions are checked against the caller's `member.role` in the target organization; `owner`/`admin` hold them by default.
114
-
115
- | Step | Scope | Permission | Target | What it does |
116
- | ---- | ----- | ---------- | ------ | ------------ |
117
- | `InviteMember` | org | `invitation: [create]` | — | Invite a member by email; carries `appRoles`, `orgRole`, `attributes`, `contactId`, `profile`; `resend` refreshes. |
118
- | `CancelInvitation` | org | `invitation: [cancel]` | — | Cancel a pending invitation. |
119
- | `ListMembers` | org | `member: [list]` | — | List members of the organization (accepts `organizationSlug`). |
120
- | `RemoveMember` | org | `member: [delete]` | — | Remove a member from the organization. |
121
- | `UpdateMemberRoles` | org | `member: [update]` | — | Set the membership's **app roles** (`appRoles` array; empty clears). |
122
- | `UpdateMemberOrgRole` | org | `member: [update]` | — | Set the **org tier** (`owner`/`admin`/`member`). |
123
- | `UpdateMemberAttributes` | org | `member: [update]` | — | Set the membership's per-organization attributes. |
124
- | `UpdateOrganization` | org | `organization: [update]` | — | Update the organization row (name, slug, metadata). |
125
- | `UpdateUserProfile` | org | `user: [update]` | `userId` (self-exempt) | Update a member's display name/image (per-organization copy). Self-service save is exempt. |
126
- | `UpdateUserAttributes` | org | `user: [set-attributes]` | `userId` | Set global user attributes. |
127
- | `BanUser` | org | `user: [ban]` | `userId` | Ban a user. |
128
- | `UnbanUser` | org | `user: [ban]` | `userId` | Lift a ban. |
129
- | `DeleteUser` | org | `user: [delete]` | `userId` | Delete a user. |
130
- | `RevokeUserSessions` | org | `session: [revoke]` | `userId` | Sign a user out of all sessions. |
131
- | `ResetUserTwoFactor` | org | `user: [reset-two-factor]` | `userId` | Clear a user's two-factor enrolment and trust-device records. |
132
- | `RevokeUserPasskeys` | org | `user: [revoke-passkeys]` | `userId` | Remove a user's passkeys (`passkeyId` to target one). |
133
- | `CreateOrganization` | system | — | — | Create an organization (tenant provisioning). Needs `userId`, or `name`+`slug` for creator-less provisioning. |
134
- | `ListUsers` | system | — | — | List every user in the deployment. |
135
- | `LeaveOrganization` | caller | — | — | End the caller's own membership of the named organization (`organizationId`, required). [Leaving from a routine](#leaving-an-organization-from-a-routine). |
136
- | `RevokeMcpGrant` | caller | — | — | Revoke the calling assistant's own [MCP grant](/mcp-oauth#switching-organization-from-the-assistant). |
137
-
138
- The two role tiers a member carries — the `owner`/`admin`/`member` org tier and the app's own role strings — are explained in [Organizations & Multi-Tenancy](/organizations#the-owner-admin-member-tier-vs-app-roles). Recovering a user who lost their second factor is a worked routine on the [Two-Factor Authentication](/two-factor#recovering-a-user-who-has-lost-their-factor) page.