Consenti

Auth Modes

Consenti's admin dashboard and API support four authentication modes. Local password auth works out of the box for development. For production you'll want to configure a proper secret, and for enterprise deployments you can delegate auth to your existing identity provider via OIDC or SAML.

Mode overview

ModeBest forRequires
localDevelopment, small teamsEmail + password in config
jwtAPI-first, programmatic accessA stable jwtSecret env var
oidcAuth0, Keycloak, Google WorkspaceOIDC provider config
samlOkta, Azure AD, PingSAML IdP config + cert
customAny other identity systemA validateUser function

Local (default)

Consenti stores a scrypt-hashed password in the database. The admin logs in at /consenti/admin/auth/login, receives a JWT, and uses it as a Bearer token for all subsequent admin API calls.

typescript
createConsenti({
  auth: {
    mode: 'local',
    adminEmail: '[email protected]',
    adminPassword: process.env.CONSENTI_ADMIN_PASSWORD!,
    // jwtSecret is auto-generated if omitted — sessions expire on restart
    jwtSecret: process.env.CONSENTI_ADMIN_JWT_SECRET,
  },
})
⚠️Always set jwtSecret (or the CONSENTI_ADMIN_JWT_SECRET env var) in production. Without it, Consenti generates a random secret on start — all admin sessions are invalidated on every restart, which is fine for dev but unacceptable in production.

How to obtain and use a JWT

bash
# 1. Log in
curl -X POST https://your-domain.com/consenti/admin/auth/login \
  -H 'Content-Type: application/json' \
  -d '{ "email": "[email protected]", "password": "your-password" }'
# → { "token": "eyJhbGci..." }

# 2. Use the token on admin routes
curl https://your-domain.com/consenti/admin/profiles \
  -H 'Authorization: Bearer eyJhbGci...'

OIDC (Auth0, Keycloak, Google)

OIDC mode redirects admin users to your identity provider for authentication. After a successful login, the IdP sends a code that Consenti exchanges for user info. Consenti maps IdP claims to admin roles using claimsMapping.

createConsenti({
  auth: {
    mode: 'oidc',
    jwtSecret: process.env.CONSENTI_ADMIN_JWT_SECRET!,
    oidc: {
      issuer: 'https://your-tenant.auth0.com',
      clientId: process.env.AUTH0_CLIENT_ID!,
      clientSecret: process.env.AUTH0_CLIENT_SECRET!,
      redirectUri: 'https://your-domain.com/consenti/admin/auth/oidc/callback',
      claimsMapping: {
        email: 'email',
        roles: 'consenti_roles',  // custom claim in your Auth0 token
      },
    },
  },
})

SAML (Okta, Azure AD)

typescript
createConsenti({
  auth: {
    mode: 'saml',
    jwtSecret: process.env.CONSENTI_ADMIN_JWT_SECRET!,
    saml: {
      issuer: 'https://idp.example.com',
      entryPoint: 'https://idp.example.com/sso/saml',
      cert: process.env.SAML_IDP_CERT!,  // IdP signing cert (PEM, no headers)
      callbackUrl: 'https://your-domain.com/consenti/admin/auth/saml/callback',
    },
  },
})

Custom auth

If your identity system doesn't fit OIDC or SAML, pass a validateUser function. It receives the raw request and must return an AdminUser object or null:

typescript
createConsenti({
  auth: {
    mode: 'custom',
    jwtSecret: process.env.CONSENTI_ADMIN_JWT_SECRET!,
    validateUser: async (req) => {
      const token = req.headers.get('Authorization')?.replace('Bearer ', '')
      if (!token) return null
      const user = await myAuthSystem.verifyToken(token)
      return user ? { email: user.email, role: user.role } : null
    },
  },
})

Frequently asked questions