Add accounts, sessions, roles and OAuth sign-in to your Luna Park app, and lock your backend routes behind guards, all without writing auth code.
What you build in the editor preview is exactly what runs once deployed: the same logic powers both, backed by the editor database while you design and by PostgreSQL in production.
| 🔐 Password auth | Sign up, log in and log out, with passwords hashed using Argon2id. |
| 🌐 OAuth2 sign-in | One-click presets for Google, Discord, GitHub, Microsoft and GitLab, or any OAuth2 provider, with a secure server-side flow (state check included). |
| 🛡️ Route guards | Mark a route as Authenticated or Requires permission X in one click. |
| 🎭 Roles & permissions | Define roles, attach permissions, check them anywhere in your logic. |
| 🍪 Sessions | Signed, httpOnly cookies, one session per device, logout from one or all devices. |
| 🗄️ Ready-made tables | Users and Sessions databases are created for you. |
- In Luna Park, open Library → Install Plugins, search for Users and install it.
- The plugin creates a
Userstable, aSessionstable and aUser Store. - A default admin account is seeded: login
admin, passwordadmin.
Important
The default admin is only there to get you started. Delete it (or change its password) before going live.
Select a route and open the Guards panel in the inspector, then click +:
- Authenticated: only logged-in users can call the route (otherwise
401). - Permission: only users whose roles grant the chosen permission can call it (
401if anonymous,403otherwise).
Routes without guards stay public. Guards run before your route logic, both in the editor preview and in the deployed backend.
Every backend route also receives the current user as the user input (id, login, roles). Anonymous visitors get the anonymous role, so you can even grant permissions to non-logged-in users.
| Node | Side | What it does |
|---|---|---|
user/connect |
Frontend | Log in, sign up, or both (login / signup / both) with a login and password, through the /_users/connect route. Outputs the connected user, and throws on failure (wrap it in error/try to handle it). |
user/disconnect |
Frontend | Log out from this device (logout) or from every device (all), through the /_users/disconnect route. |
user/connect-by-id |
Backend | Connect the caller as any user, without a password (impersonation, magic links...). Guard the route. |
user/disconnect-by-id |
Backend | Log out every session of any user. Guard the route. |
user/current |
Frontend | Get the user connected in this browser, and whether someone is connected. |
user/check-password |
Shared | Check a password against the password policy from the settings. Outputs whether it is valid and, if not, why (e.g. to give feedback on a sign-up form). |
user/change-password |
Backend | Change the connected user's password (current password required) and log out their other devices. |
user/request-password-reset |
Backend | Create a one-hour, single-use reset token for a login. Send it to the user yourself (by email, for example). |
user/reset-password |
Backend | Set a new password from a reset token and log out every device. |
user/list-sessions |
Backend | List the connected user's active sessions, flagging the current one. |
user/revoke-session |
Backend | Log out one of the connected user's sessions. |
user/delete |
Backend | Delete a user by id, with all their sessions. |
oauth/connect |
Frontend | Open the provider's sign-in popup, then connect the user (login / signup / both). |
oauth/link |
Frontend | Link a provider account to the connected user, so they can also sign in with it. |
roles/has-permission |
Backend | Check if a user has a permission. |
roles/assert-permission |
Backend | Stop with an error if a user lacks a permission. |
roles/set-roles |
Backend | Replace a user's roles (unknown roles are rejected). |
hash/hash-argon2 |
Backend | Hash any string with Argon2id. |
hash/verify-argon2 |
Backend | Check a string against an Argon2 hash. |
- General: choose how users are identified (username or email), set the password policy (minimum length and required character types), and manage roles and permissions in the Access panel.
- OAuth2: add providers with separate development and production credentials. Empty production fields fall back to the development values (except the redirect URL).
Click Add a provider and pick a preset: Google, Discord, GitHub, Microsoft or GitLab. Endpoints, scopes and identity mapping are filled in for you, and a setup guide links to the provider's console and gives the redirect URIs to register. You only paste the client ID and secret.
The authorization URL is completed automatically with client_id, redirect_uri, response_type=code and scope (values already in the URL are kept), so a custom provider only needs its base authorize endpoint.
For production, set each provider's redirect URI to:
https://<your-domain>/api/_users/oauth/callback
When you build your project, the plugin wires itself into the generated server:
- Endpoints
GET /api/_users/mereturns the connected user.GET /api/_users/oauth/startandGET /api/_users/oauth/callbackhandle OAuth sign-in.
- Secrets stay out of the code: each provider's client secret is written to the project's
.envasUSERS_OAUTH_SECRET_<PROVIDER_ID>and read at runtime. - Runtime: the logic is imported from
@luna-park/plugin-users/server, which is added to your backend dependencies automatically.
pnpm install
pnpm build # build the plugin
pnpm dev # rebuild on change
pnpm preview # serve it to the Luna Park editor (http://127.0.0.1:2084)The package ships two entries:
@luna-park/plugin-users: the editor plugin (settings, nodes, guards).@luna-park/plugin-users/server: the framework-agnostic runtime used by the generated backend.
Made with 💙 by Luna Park.