Ship secure user access fast: let Docly handle login, you handle the permissions
-
What you'll see
You are building a Docly site where external users (customers, partners) must log in to see their own data, and you are tempted to implement your own user accounts with passwords — meaning you would also own password hashing, "forgot password" e-mails, session handling, brute-force protection and 2FA. None of that is necessary: Docly's login portal already provides all of it. What the platform does not model is your application-level rights — admin vs. regular user, which customers a user can see, whether they can upload. Granting Browse/Modify shares to every user would expose the raw folder structure; managing rights only in your own documents would leave users unable to log in at all.
-
What's actually happening
The pattern is a two-layer access model — for example an admin module and a welcome module that both read from one shared data site:
- Layer 1 — authentication (Docly shares). A folder share on the site root carrying only the
"V"(Visit) flag lets the user sign in through Docly's login portal and reach the published site — nothing more.docly.addFolderShare("/", email, "V", message)both creates the share and sends the invitation e-mail with your message text. The platform owns the entire credential lifecycle — password storage, password resets, session cookies, lockout protection — so your code never sees or stores a password. After login,request.userin every API script holds the authenticated e-mail address. - Layer 2 — authorization (local user registry). A folder (e.g.
Brukere) holds one schema-bound document per user, filename = e-mail address, sodocly.getFile("Brukere/" + request.user)is the entire identity join. The document carries the application rights: an active flag (Tilgang), an admin flag (Admin), per-customer permissions (Tilganger: [{Kunde, Opplasting}]), plus audit fields (Invitert,SistInnlogging).
Because the share only grants Visit, users can authenticate but cannot browse or modify anything directly — every read and write goes through server-side API scripts that check the registry document first. The two layers also stay independently inspectable:
docly.getFolderShares("/")returns each share'sAllowVisit/AllowAdminflags and its invitation state (Accepted,Since), which you can join against the registry to show per-user status such as "unanswered invitation since <date>" or "invitation declined / expired".A separate public landing page (welcome module) needs no access control at all — its login button simply links to the protected module's path; Docly's portal takes over from there and sets the session cookie.
- Layer 1 — authentication (Docly shares). A folder share on the site root carrying only the
-
What to do
1. Model the user registry. Create a
Brukerefolder accepting aBrukerschema whose title field is the e-mail address (TitleFieldPlaceholder: "E-post",ForceGUIDFilename: false). Fields:Navn,Tilgang(active = may log in),Admin, hiddenInvitertandSistInnlogging, and aTilgangertable of{Kunde, Opplasting}rows databound to the customer schema. Validate on save: filename must passdocly.isEmail(), and non-admins must have at least one customer access row.2. Grant and revoke web access from the save pipeline. When a user document is saved with the active flag on, add the share; when deactivated or deleted, remove it. Keep both in a shared library:
export function addWebAccess(username) { let shares = docly.getFolderShares("/"); if (!shares.Shares.some(s => s.User.toLowerCase() == username.toLowerCase() && s.AllowVisit)) { let config = docly.getJson("#/Config"); // the document's field values // Creates the share AND sends the invitation e-mail docly.addFolderShare("/", username, "V", config.InviteMessage); docly.patchFile("/Brukere/" + username, { "Invitert": /* yyyy-MM-ddTHH:mm */ }); return true; } return false; } export function removeWebAccess(username) { let shares = docly.getFolderShares("/"); // Only remove pure visit-shares - developer/admin shares survive if (shares.Shares.some(s => s.User.toLowerCase() == username.toLowerCase() && s.AllowVisit && !s.AllowAdmin && !s.AllowBrowse && !s.AllowCreate && !s.AllowInvite && !s.AllowModify && !s.AllowPublish)) { docly.removeFolderShare("/", username); return true; } return false; }Keep the invitation text in a config document (
#/Config→InviteMessage) so admins can edit it without touching code.3. Gate every API script through an access library. All authorization resolves from
request.useragainst the registry — never trust client input:export function isUserAdmin() { let user = docly.getFile("Brukere/" + request.user); return user != null && user.Admin && user.Tilgang; } export function isDoclyAdmin() { // platform developers, distinct from app admins let shares = docly.getFolderShares("/"); return shares.Shares.some(s => s.User.toLowerCase() == request.user.toLowerCase() && s.AllowAdmin); } export function checkFolderAccess(folder, write, customerId) { if (isUserAdmin()) return true; if (!customerId || folder.Name != "Sertifikater") return false; let user = getUser(); // resolves Tilganger from the registry document return user.Tilganger.find(t => t.Kunde == customerId && (!write || t.Opplasting)); }4. Surface invitation status in the admin UI. Join the registry against the shares so admins see who has accepted:
let shares = docly.getFolderShares("/"); for (let f of users) { let share = shares.Shares.find(s => s.User.toLowerCase() == f.filename.toLowerCase() && s.AllowVisit); if (share) { f.WebTilgangSiden = share.Accepted ? docly.format(new Date(share.Since), "dd.MM.yyyy") : "Ubesvart invitasjon " + docly.format(new Date(share.Since), "dd.MM.yyyy"); } else if (f.Tilgang) { f.WebTilgangSiden = "Invitasjon avslått / utløpt"; // active in registry but share gone } }5. Track logins. In the API endpoint the app calls on load, patch
SistInnloggingon the registry document (once per day) and write an activity-log entry. Sign out withdocly.logOut().6. Safety rules:
- Users cannot change their own
Tilgang/Adminflags or delete their own user document. removeWebAccessmust check that the share is a pure visit-share before removing it — otherwise deactivating a user who is also a developer would revoke their workspace access.- The public landing page links to the protected module path (e.g.
/admin); unauthenticated visitors hitting it are sent through Docly's login portal automatically — you write no login UI yourself. - Skip activity-logging for
isDoclyAdmin()users so developer traffic doesn't pollute customer audit logs.
- Users cannot change their own
Last reviewed 17 Oct 2026