Reference for Netlify Identity setup — role-based access, GoTrue JS signup, custom user_metadata, Identity widget
Correct approach:
/* rulesforce = true and conditions = {Role = ["rolename"]}force = true# 1. Passthrough — public pages
[[redirects]]
from = "/login.html"
to = "/login.html"
status = 200
# 2. Role-gated access
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
force = true
conditions = {Role = ["viewer"]}
# 3. Fallback — unauthenticated users
[[redirects]]
from = "/*"
to = "/login.html"
status = 302
force = true
Common errors:
force=true on /* catches it in a loopforce=true on fallback → doesn't override existing filesCorrect approach:
netlify/functions/identity-signup.js// netlify/functions/identity-signup.js
exports.handler = async (event) => {
const { user } = JSON.parse(event.body);
return {
statusCode: 200,
body: JSON.stringify({
app_metadata: { ...user.app_metadata, roles: ['viewer'] },
}),
};
};
Common errors:
user_metadata:
const auth = new GoTrue({ APIUrl: `${window.location.origin}/.netlify/identity`, setCookie: true });
await auth.signup(email, password, { full_name, linkedin_url, company_name });
user_metadata is NOT visible in Netlify Identity UI but is stored on the user objecthttps://unpkg.com/gotrue-js@0.9.29/dist/gotrue.jsnetlifyIdentity.open('login')netlifyIdentity.on('login', callback)netlifyIdentity.currentUser()netlifyIdentity.logout()https://identity.netlify.com/v1/netlify-identity-widget.jswidget.on('init', callback) IMMEDIATELY — not inside DOMContentLoadedwidget.currentUser() fallback for when init already firedCommon error: Registering widget.on('init', ...) inside DOMContentLoaded → widget's init event already fired → callback never runs → page stays blank