@netlify/agent-runner-cli 1.75.0-alpha.0 → 1.76.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.
@@ -0,0 +1,329 @@
1
+ ---
2
+ name: netlify-identity
3
+ description: Add user authentication with Netlify Identity. Use when implementing signups, logins, password recovery, role-based access control, OAuth providers, or protecting Netlify Functions with user tokens.
4
+ ---
5
+
6
+ # Netlify Identity
7
+
8
+ Netlify Identity is a user management service for signups, logins, password recovery, user metadata, and role-based
9
+ access control. It is built on [GoTrue](https://github.com/netlify/gotrue) and issues JSON Web Tokens (JWTs).
10
+
11
+ There are two integration paths:
12
+
13
+ - **Widget** (`netlify-identity-widget`) — Drop-in UI modal for login, signup, and password recovery. Zero framework
14
+ dependencies. Best when you want auth working quickly without building custom forms.
15
+ - **gotrue-js** — Programmatic client for full control over authentication flows. Best when you need custom UI or
16
+ headless auth logic.
17
+
18
+ Both communicate with the Identity endpoint at `/.netlify/identity` on your site.
19
+
20
+ ## Quick Start
21
+
22
+ Add the widget CDN script and a menu element for instant auth:
23
+
24
+ ```html
25
+ <script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
26
+ <div data-netlify-identity-menu></div>
27
+ ```
28
+
29
+ Handle logins:
30
+
31
+ ```javascript
32
+ netlifyIdentity.on('login', async (user) => {
33
+ const jwt = await netlifyIdentity.refresh()
34
+ await fetch('/.netlify/functions/protected', {
35
+ headers: { Authorization: `Bearer ${jwt}` },
36
+ })
37
+ })
38
+ ```
39
+
40
+ ### When to Use Widget vs gotrue-js
41
+
42
+ - **Prefer the widget** when the user wants auth working quickly, doesn't have custom UI requirements, or is building a
43
+ simple site. It requires minimal code and automatically sets the `nf_jwt` cookie needed for role-based redirects.
44
+ - **Use gotrue-js** when the user needs custom login/signup forms, headless auth, or programmatic control over the auth
45
+ flow. Pass `setCookie: true` if you need role-based redirects (see below).
46
+ - **Never mix both** in the same page unless accessing `netlifyIdentity.gotrue` for low-level operations while using the
47
+ widget for UI.
48
+
49
+ ## Setup
50
+
51
+ Identity is automatically enabled when the deploy includes Identity code. Default settings:
52
+
53
+ - **Registration** — Open (anyone can sign up). Change to Invite only in **Project configuration > Identity** if needed.
54
+ - **Autoconfirm** — Off (new signups require email confirmation). Enable in **Project configuration > Identity** to skip confirmation during development.
55
+
56
+ ## Widget (Drop-in UI)
57
+
58
+ ### CDN Script Tag
59
+
60
+ Include the widget script and add HTML attributes for automatic controls:
61
+
62
+ ```html
63
+ <script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
64
+
65
+ <!-- Login/Signup menu (shows username + logout when logged in) -->
66
+ <div data-netlify-identity-menu></div>
67
+
68
+ <!-- Simple button that opens the modal -->
69
+ <div data-netlify-identity-button>Login with Netlify Identity</div>
70
+ ```
71
+
72
+ The widget attaches itself to `window.netlifyIdentity` automatically.
73
+
74
+ ### npm Module
75
+
76
+ ```bash
77
+ npm install netlify-identity-widget
78
+ ```
79
+
80
+ ```javascript
81
+ import netlifyIdentity from 'netlify-identity-widget'
82
+
83
+ netlifyIdentity.init({
84
+ container: '#netlify-modal', // defaults to document.body
85
+ locale: 'en', // language code (en, fr, es, pt, de, etc.)
86
+ // APIUrl: 'https://your-site.netlify.app/.netlify/identity', // only for non-Netlify hosts
87
+ })
88
+ ```
89
+
90
+ Set `APIUrl` only when the app is served from a different domain than the Identity endpoint (Cordova, Electron, or
91
+ custom domains).
92
+
93
+ ### Events
94
+
95
+ ```javascript
96
+ netlifyIdentity.on('init', (user) => console.log('init', user))
97
+ netlifyIdentity.on('login', (user) => console.log('login', user))
98
+ netlifyIdentity.on('logout', () => console.log('Logged out'))
99
+ netlifyIdentity.on('error', (err) => console.error('Error', err))
100
+
101
+ netlifyIdentity.off('login') // unbind all login handlers
102
+ netlifyIdentity.off('login', handler) // unbind a specific handler
103
+ ```
104
+
105
+ ### Key Methods
106
+
107
+ ```javascript
108
+ netlifyIdentity.open() // open modal (default tab)
109
+ netlifyIdentity.open('login') // open to login tab
110
+ netlifyIdentity.open('signup') // open to signup tab
111
+ netlifyIdentity.open('signup', { full_name: 'Jane' }) // pre-fill signup metadata
112
+ netlifyIdentity.close()
113
+ netlifyIdentity.logout()
114
+ const user = netlifyIdentity.currentUser() // null if not logged in
115
+ const jwt = await netlifyIdentity.refresh() // refresh the JWT
116
+ const gotrueClient = netlifyIdentity.gotrue // underlying GoTrue client
117
+ ```
118
+
119
+ ## gotrue-js (Programmatic Client)
120
+
121
+ ### Install
122
+
123
+ ```bash
124
+ npm install gotrue-js
125
+ ```
126
+
127
+ ### Initialize
128
+
129
+ ```typescript
130
+ import GoTrue from 'gotrue-js'
131
+
132
+ const auth = new GoTrue({
133
+ APIUrl: 'https://<your-site>.netlify.app/.netlify/identity',
134
+ setCookie: false,
135
+ })
136
+ ```
137
+
138
+ `setCookie: true` tells the server to set HttpOnly cookies, which protects tokens from XSS but is required if you need
139
+ role-based redirects without the widget (the CDN reads an `nf_jwt` cookie to evaluate redirect conditions). Use `false`
140
+ when you manage tokens entirely in JavaScript and don't need CDN-level access control.
141
+
142
+ ### Core Methods
143
+
144
+ ```javascript
145
+ // Signup (sends confirmation email if autoconfirm is off)
146
+ const response = await auth.signup(email, password)
147
+ // Pass user_metadata during signup: auth.signup(email, password, { full_name: 'Jane' })
148
+
149
+ // Confirm email (token from confirmation email URL fragment)
150
+ const user = await auth.confirm(token, true) // remember = persist session
151
+
152
+ // Login
153
+ const user = await auth.login(email, password, true) // remember = persist session
154
+
155
+ // Current user (from localStorage)
156
+ const user = auth.currentUser()
157
+
158
+ // Get fresh JWT (auto-refreshes if expired; pass true to force)
159
+ const token = await user.jwt()
160
+
161
+ // Update user
162
+ await user.update({ email: 'new@example.com', password: 'newpassword' })
163
+ await user.update({ data: { full_name: 'Jane Doe' } }) // update user_metadata
164
+
165
+ // Logout
166
+ await user.logout()
167
+
168
+ // Password recovery
169
+ await auth.requestPasswordRecovery(email) // sends recovery email
170
+ const user = await auth.recover(recoveryToken, true) // remember = persist session
171
+
172
+ // Check server settings (signup enabled? autoconfirm? OAuth providers?)
173
+ const settings = await auth.settings()
174
+ ```
175
+
176
+ ### Error Handling
177
+
178
+ gotrue-js exports `JSONHTTPError`, `TextHTTPError`, and `HTTPError` for granular error catching. Use `error.json`,
179
+ `error.data`, or `error.status` respectively.
180
+
181
+ ## Token Handling
182
+
183
+ - Access tokens expire after **1 hour**
184
+ - Both `user.jwt()` (gotrue-js) and `netlifyIdentity.refresh()` (widget) auto-refresh using the refresh token
185
+ - Always call `jwt()` or `refresh()` before authenticated requests — do not cache tokens
186
+ - Send the token as `Authorization: Bearer <token>`
187
+
188
+ ## Roles and Authorization
189
+
190
+ ### Metadata Types
191
+
192
+ - **`app_metadata.roles`** — Server-controlled. Only settable via the Netlify UI, admin API, or server-side code
193
+ (Identity event functions, protected functions with admin token). Do not allow users to set their own roles.
194
+ - **`user_metadata`** — User-controlled. Users can update this via `user.update({ data: { ... } })`.
195
+
196
+ ### Role-Based Redirects
197
+
198
+ Use `netlify.toml` to restrict paths by role:
199
+
200
+ ```toml
201
+ [[redirects]]
202
+ from = "/admin/*"
203
+ to = "/admin/:splat"
204
+ status = 200
205
+ conditions = { Role = ["admin"] }
206
+
207
+ [[redirects]]
208
+ from = "/admin/*"
209
+ to = "/"
210
+ status = 302
211
+ ```
212
+
213
+ Rules are evaluated top-to-bottom. The first redirect matches users with the `admin` role; everyone else falls through
214
+ to the second rule and is redirected away.
215
+
216
+ **How it works:** The widget sets an `nf_jwt` cookie that the CDN reads to evaluate `conditions = { Role = [...] }`.
217
+ Without this cookie, role-based redirects will not work. If using gotrue-js instead of the widget, pass
218
+ `setCookie: true` when initializing GoTrue so the server sets this cookie.
219
+
220
+ ## Identity Event Functions
221
+
222
+ Special serverless functions that trigger on Identity lifecycle events. These use the **legacy named `handler` export**
223
+ (not the modern default export) because they receive `event.body` containing the user payload.
224
+
225
+ **Event names:** `identity-validate`, `identity-signup`, `identity-login`
226
+
227
+ - `identity-signup` — fires when a new user signs up (email/password or OAuth)
228
+ - `identity-login` — fires on each login
229
+ - `identity-validate` — fires during signup before the user is created; return a non-200 status to reject the signup
230
+
231
+ The filename must match the event name exactly (e.g. `netlify/functions/identity-signup.mts`).
232
+
233
+ ### Example: Assign Default Role on Signup
234
+
235
+ ```typescript
236
+ // netlify/functions/identity-signup.mts
237
+ import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'
238
+
239
+ const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
240
+ const { user } = JSON.parse(event.body || '{}')
241
+
242
+ return {
243
+ statusCode: 200,
244
+ body: JSON.stringify({
245
+ app_metadata: {
246
+ ...user.app_metadata,
247
+ roles: ['member'],
248
+ },
249
+ }),
250
+ }
251
+ }
252
+
253
+ export { handler }
254
+ ```
255
+
256
+ The response body replaces `app_metadata` and/or `user_metadata` on the user record — include all fields you want to
257
+ keep, not just new ones.
258
+
259
+ ## Protected Functions
260
+
261
+ Functions that verify the calling user's identity. Use the legacy `handler` export to access `context.clientContext`.
262
+
263
+ ```typescript
264
+ // netlify/functions/protected-action.mts
265
+ import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'
266
+
267
+ const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
268
+ const { identity, user } = context.clientContext || {}
269
+
270
+ if (!user) {
271
+ return { statusCode: 401, body: JSON.stringify({ error: 'Unauthorized' }) }
272
+ }
273
+
274
+ // user.sub — user ID
275
+ // user.email — email address
276
+ // user.app_metadata.roles — assigned roles
277
+ // user.user_metadata — user-controlled data
278
+
279
+ // Use identity.token (admin token) to call the Identity admin API:
280
+ const adminAuthHeader = `Bearer ${identity.token}`
281
+ const response = await fetch(`${identity.url}/admin/users/${user.sub}`, {
282
+ method: 'PUT',
283
+ headers: { Authorization: adminAuthHeader },
284
+ body: JSON.stringify({ app_metadata: { roles: ['editor'] } }),
285
+ })
286
+
287
+ const data = await response.json()
288
+ return { statusCode: 200, body: JSON.stringify(data) }
289
+ }
290
+
291
+ export { handler }
292
+ ```
293
+
294
+ **Note:** Operations using `identity.token` (admin token) do **not** work locally with `netlify dev`. Deploy to Netlify
295
+ to test server-side admin operations.
296
+
297
+ ## External OAuth Providers
298
+
299
+ Netlify Identity supports Google, GitHub, GitLab, and BitBucket as built-in OAuth providers.
300
+
301
+ **Enable in Netlify UI:** Project configuration > Identity > Registration > External providers
302
+
303
+ ### Widget
304
+
305
+ Once enabled in the UI, the widget automatically shows OAuth provider buttons. No additional code needed.
306
+
307
+ ### gotrue-js
308
+
309
+ ```javascript
310
+ const url = auth.loginExternalUrl('github')
311
+ // Redirect the user to this URL to start the OAuth flow
312
+ window.location.href = url
313
+ ```
314
+
315
+ Available provider names: `'google'`, `'github'`, `'gitlab'`, `'bitbucket'`
316
+
317
+ ## Common Errors & Solutions
318
+
319
+ - **"Signups not allowed for this instance" (403)** — Registration is set to Invite only. Change to Open in Project
320
+ configuration > Identity, or invite users from the Identity tab.
321
+ - **"Token expired" / 401 on API calls** — Stale access token. Always call `user.jwt()` or `netlifyIdentity.refresh()`
322
+ before authenticated requests.
323
+ - **Identity event function not triggering** — Verify filename matches exactly (`identity-signup`, `identity-validate`,
324
+ or `identity-login`), is in `netlify/functions/`, and uses `.mts`/`.mjs`.
325
+ - **`clientContext` is undefined** — Use named `handler` export (not `export default`), send `Authorization: Bearer`
326
+ header, and verify token is fresh.
327
+ - **Admin methods not working locally** — `identity.token` is unavailable in local dev. Deploy to Netlify to test.
328
+ - **"User not found" after OAuth login** — Enable the OAuth provider in Project configuration > Identity > External
329
+ providers. Users are created on first OAuth login.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.75.0-alpha.0",
4
+ "version": "1.76.0",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",