Everything the kit does is driven by one object, AUTH_CONFIG, defined in config.js. Rename config.example.js to config.js, set the values and keep config.js out of version control since it holds your sheet ID. Keep only one of the two files: Apps Script shares one global scope across the project, so two const AUTH_CONFIG declarations stop it loading. There is one source of truth for roles, the hierarchy and the fail-closed behaviour, so you change auth policy in one place.

Quick start

const AUTH_CONFIG = {
  APP_NAME: 'My App',
  AUTH_SHEET_ID: 'PASTE_YOUR_AUTH_SHEET_ID_HERE',
  OWNER_EMAIL: null,
  USE_DOMAIN_AUTH: false,
  ROLE_RANKS: { viewer: 10, editor: 20, admin: 30 },
  DEFAULT_REQUIRED_ROLE: 'viewer',
  ALLOW_OPEN_ENROLMENT: false,
  OPEN_ENROLMENT_ROLE: 'viewer',
  ALLOWED_DOMAIN: null,
  ADMIN_EMAILS: [],
  EDITOR_EMAILS: [],
  DOMAIN_DEFAULT_ROLE: 'viewer',
};

In sheet-backed mode the auth sheet itself is generated for you. In the Apps Script editor, run SETUP_createAuthSheet() once from the function dropdown: it creates a private spreadsheet in your Drive with the headers, a role dropdown per row, Active checkboxes and sample rows in place, then logs the new sheet’s ID. Paste that ID into AUTH_SHEET_ID and replace the sample rows with real users. The function is owner-guarded, so visitors cannot call it through google.script.run – see Setup and admin.

Fields

Identity

Field Type Default Notes
APP_NAME string 'My App' Shown in logs and on the generated auth sheet. No security weight.
AUTH_SHEET_ID string placeholder ID of the private auth sheet. Generate it with SETUP_createAuthSheet(), run from the editor. Sheet mode only.
OWNER_EMAIL string | null null Your email. Optional. The owner-guarded setup entry points (SETUP_*, DEBUG_*) refuse non-owners by comparing the active and effective user, which holds under executeAs: USER_DEPLOYING. Under USER_ACCESSING that comparison passes for every visitor, so set this to keep those entry points owner-only. null leaves the USER_DEPLOYING-only behaviour.

Auth mode selection

Field Type Default Notes
USE_DOMAIN_AUTH boolean false Selects the auth mode. false is sheet-backed: a user’s role comes from their row in the auth sheet. true is domain / allowlist mode: the role is derived from the user’s email address via ADMIN_EMAILS, EDITOR_EMAILS and ALLOWED_DOMAIN, and no sheet is read. Your requireAuth() calls behave identically in either mode.

Roles

Field Type Default Notes
ROLE_RANKS object { viewer: 10, editor: 20, admin: 30 } The role hierarchy: a map of role name to numeric privilege rank, where a higher rank satisfies any lower requirement (see the tip below). Adding a role is safe; renaming admin or editor is not, because the built-in guards use those names literally – see Renaming the roles.
DEFAULT_REQUIRED_ROLE string 'viewer' The role required when you call requireAuth() with no argument.

The hierarchy is the rank, not the order

A user satisfies a required role if their role’s rank in ROLE_RANKS is at or above the required role’s rank. Leave gaps (10, 20, 30) so you can slot a role in later without renumbering. The kit derives an ordered ROLES list from this map automatically for the sheet dropdown and validity checks, so there is still a single source of truth.

Fail-closed behaviour

Field Type Default Notes
ALLOW_OPEN_ENROLMENT boolean false When false, a user not in the sheet is denied. When true, an identified but unknown user is granted OPEN_ENROLMENT_ROLE.
OPEN_ENROLMENT_ROLE string 'viewer' The role handed out under open enrolment. Should be your lowest role.

Read before enabling open enrolment

Open enrolment plus an unidentifiable visitor is the worst case. Understand the empty-email landmine before turning this on. The kit ships closed for a reason.

Domain / allowlist mode

Only used when USE_DOMAIN_AUTH is true.

Field Type Default Notes
ALLOWED_DOMAIN string | null null Restrict access to one Workspace domain, for example 'example.com'. null means no restriction, which is not recommended for authenticated roles.
ADMIN_EMAILS string[] [] Emails granted admin. Case-insensitive.
EDITOR_EMAILS string[] [] Emails granted editor. Case-insensitive.
DOMAIN_DEFAULT_ROLE string | null 'viewer' Role for an identified, in-domain user on none of the lists above. Set to null to deny them and fail closed.

Renaming the roles

The kit does not care what the roles are called. To use member, manager, owner:

ROLE_RANKS: { member: 10, manager: 20, owner: 30 },
DEFAULT_REQUIRED_ROLE: 'member',

Then guard routes with the new names, for example requireAuth('manager'). The auth sheet’s role dropdown is a snapshot of the derived ROLES list from when the sheet was generated; it does not update itself – edit the Role column’s data-validation rule and stored roles to match. The client guard in auth-api.html reads the role order from the server (getUserInfo returns it), so there is no second copy to keep in sync.

One caveat: the built-in names admin and editor are load-bearing. The administrative guards call requireAuth('admin') literally (auth-setup.js, auth-scopes.js), so after a rename the admin endpoints refuse everyone – in either mode – until those literals match the new name. Domain mode is coupled more deeply still: resolveDomainRole_ maps the allowlists to literal 'admin' and 'editor', so renaming while USE_DOMAIN_AUTH is true fails every allowlisted user closed (“Configured role is not in AUTH_CONFIG.ROLES”) until auth-adapter.js is updated too. A stale literal denies, it never grants, so a botched rename locks people out rather than letting anyone in. See Extending for the full rename procedure.