Skip to content
For the complete Netlify documentation index, see llms.txt. Markdown versions of this page are available by appending .md to the URL.

Unlimited seats on Netlify Pro for $20/month → Learn more 👥

Use Identity in functions

For the complete documentation index, see llms.txt

You can use @netlify/identity in Netlify Functions and Edge Functions to verify users, check roles, and manage users programmatically.

Use getUser() to check whether the request comes from an authenticated user. It returns the User object, or null if the user is not logged in.

import { getUser } from '@netlify/identity'
import type { Context } from '@netlify/functions'
export default async (req: Request, context: Context) => {
const user = await getUser()
if (!user) return new Response('Unauthorized', { status: 401 })
return Response.json({ id: user.id, email: user.email })
}

The User object includes a roles array from the user’s app_metadata. You can use this to restrict access to specific functions based on a user’s role.

netlify/functions/admin-users.ts
import { getUser, admin } from '@netlify/identity'
import type { Context } from '@netlify/functions'
export default async (req: Request, context: Context) => {
const user = await getUser()
if (!user) return new Response('Unauthorized', { status: 401 })
if (!user.roles.includes('admin')) {
return new Response('Forbidden', { status: 403 })
}
// Only admins reach this point
const users = await admin.listUsers()
return Response.json({ users })
}

Roles are set on the user’s app_metadata.roles field and included in the JWT. Changes to a user’s roles take effect on their next login or token refresh, not immediately.

The admin export from @netlify/identity provides server-side user management: list, create, update, and delete users. These operations use a short-lived admin token and can only run in Netlify Functions (not in the browser or Edge Functions).

import { admin } from '@netlify/identity'
import type { Context } from '@netlify/functions'
export default async (req: Request, context: Context) => {
const users = await admin.listUsers()
return Response.json({ total: users.length })
}

For the full admin API, refer to the @netlify/identity admin operations documentation.

You can call login(), signup(), or logout() from a Netlify Function or Edge Function to handle authentication entirely on the server. The library reads and writes the nf_jwt and nf_refresh cookies through the Netlify runtime, so the user’s browser receives the session via the response.

netlify/functions/login.ts
import { login, verifyRequestOrigin } from '@netlify/identity'
import type { Context } from '@netlify/functions'
export default async (req: Request, context: Context) => {
verifyRequestOrigin(req)
const { email, password } = await req.json()
await login(email, password)
return new Response(null, { status: 302, headers: { Location: '/dashboard' } })
}

Identity event functions are different from the patterns above. Instead of calling @netlify/identity from your function, the Netlify platform calls your function automatically when an Identity event occurs. The function receives a typed event with the user object.

To subscribe to Identity events, export a default object from your function with a method for each event you want to handle. For the broader event-handler pattern, see Trigger functions on events.

Five events are available:

HandlerTriggered when
userValidateA user attempts to sign up, before the account is created. Use this to block signups based on email domain, rate limiting, or custom validation.
userSignupA user completes signup (email or external providers). Triggers after email confirmation, if confirmation is enabled. Use this to assign roles, sync to external systems, or send welcome notifications.
userLoginA user logs in. Use this to track logins, update last-seen timestamps, sync profile data, or block specific users.
userModifiedA user’s profile is updated. Use this to validate or react to user changes.
userDeletedA user is deleted. Notification only.

Each handler receives an event with a parsed user object. The fields are camelCase (appMetadata, userMetadata, confirmedAt, etc.).

netlify/functions/identity.mts
import type { UserSignupEvent } from "@netlify/functions"
export default {
userSignup(event: UserSignupEvent) {
console.log(`New signup: ${event.user.email}`)
},
}

Call event.deny() from a userValidate, userSignup, userLogin, or userModified handler to reject the corresponding action. The end user receives a 401 response and the action is aborted. event.deny() is the canonical way to reject an action — it does not produce an error in observability.

netlify/functions/identity.mts
import type { UserValidateEvent } from "@netlify/functions"
export default {
userValidate(event: UserValidateEvent) {
if (!event.user.email?.endsWith("@example.com")) {
return event.deny()
}
},
}

If multiple functions subscribe to the same event, the first one to call event.deny() aborts the chain — subsequent functions are not invoked.

Return an object with a user property to modify the user’s record before it’s persisted. This is the most common way to assign roles programmatically.

netlify/functions/identity.mts
import type { UserSignupEvent } from "@netlify/functions"
export default {
userSignup(event: UserSignupEvent) {
return {
user: {
...event.user,
appMetadata: {
...event.user.appMetadata,
roles: ["member"],
},
},
}
},
}

This sets the member role on every new user. The roles are included in the user’s JWT and can be used for role-based access control with redirect rules.

Any Identity event handler can run in background mode by setting background: true in the function’s config. The Identity action completes immediately and your handler runs asynchronously — useful for logging or syncing without blocking the user.

netlify/functions/identity.mts
import type { Config, UserLoginEvent } from "@netlify/functions"
export default {
userLogin(event: UserLoginEvent) {
// Track the login asynchronously; don't block the user's response.
},
}
export const config: Config = {
background: true,
}