8 min read

Rekey 2.4 turns the hosted portal into a full account page for your customers. They can create an account, sign in the way your application allows, set up two-factor, change their email, invite their team, free a device slot and approve a desktop app. You did not have to build any of those pages, and you can serve them from your own domain.

Until now the portal was mostly a billing page: sign in, see the plan and payments, cancel. Everything else was a page you wrote yourself or a support ticket. You turn it on per application in the panel under Application, Billing, Portal, and it reads the sign-in methods, profile questions and branding you have already set.

The portal overview for a customer of an app called Northwind: an active Team subscription at $29.00 a month with its renewal date and a Cancel subscription button, what the plan includes, a usage meter showing 137 of 500 report exports, a link to licenses, and the plan list. Tabs across the top read Overview, Security, Profile and Organization.
The overview a signed-in customer lands on: subscription, plan limits, usage this period and the plans on offer.

Sign in and sign up

The sign-in page offers what the application allows: a password, an emailed sign-in link, or both. Social sign-in is opt-in per provider. Add the portal's callback, <PUBLIC_PORTAL_URL>/<slug>/oauth/callback, to the provider's console, then tick Offer in the hosted portal on that provider in the panel. Until you do, the portal shows no button for it. When the application has no method the portal can offer, the page says that sign-in happens in your app.

The portal sign-in page for Northwind, with an email and password form, a Forgot password link, a separate form to email a sign-in link, a Continue with Google button and a Create an account link.
Password, an emailed link and Google, because this application allows all three.

While sign-up is open (signupMode: public), customers can create an account from the portal. When the application requires a confirmed address, the page asks them to check their inbox and offers to resend the link. It shows the same page whether or not the address already had an account. When your profile schema has required fields, the next step asks for them, and the dashboard waits until they are answered.

A Finish setting up your account page in the Northwind portal, asking for Company, marked required, and Role, with a Save and continue button.
Required profile questions come straight after sign-up, before the dashboard.

Mail scanners open links to check them, and a single-use link opened by a scanner is gone before the customer clicks it. So every emailed link the portal handles (sign-in, email confirmation, password reset, email change and invitations) only opens a page with a button. The token is spent when the customer presses it.

Security and profile

The Security page lets a customer change their password, or create one with a code emailed to them if they signed up by link or with a provider. They can turn two-factor on or off, replace their backup codes with a current authenticator code, see where they are signed in with this session marked, and sign out one session or all of them. They can also remove a linked sign-in provider, unless it is the only way they can sign in.

The Security page of the Northwind portal: a Change password form with Show toggles, two-factor authentication marked Off with a Start setup button, and a Where you are signed in list showing Chrome on macOS marked This session with Sign out and Sign out everywhere buttons.
Password, two-factor and sessions on one page. Changing the password signs the customer out everywhere, and the page says so first.

Setting up two-factor shows a QR code and the setup key, asks for a code from the authenticator app, and then shows ten backup codes once. The authenticator entry carries your portal's display name, or the application name.

Two-factor setup in the Northwind portal: a QR code, the setup key in text, a link to open an authenticator app on the same device, a field for the 6-digit code and a Turn on two-factor button.
Two-factor setup. The backup codes appear after the first code is accepted.

The Profile page holds the email address, the answers to your profile questions and account deletion. Changing the email sends a link to the new address and a notice to the old one. The address changes only when the link is used, and that ends every session. Deleting the account asks for the password or a two-factor code. An account with neither gets a code by email instead. Deletion follows the accountDeletion setting from 2.3: with off or server_only the portal refuses it and points the customer at your support contact.

The Profile page of the Northwind portal: the current email address with a New email address field, an About you card with Company and Role answers and a Save profile button, and a Delete account card that lists what is erased and what is kept.
The profile, with the delete card spelling out what is erased and which payment records are kept.

Teams

When your application bills organizations or lets customers create them, the Organization page lists the customer's organizations and lets them switch between them or go back to their personal account. An owner or admin can invite by email, change roles, remove members, and resend or revoke a pending invitation. The API now emails the invitation itself, in your application's branding.

The Organization page of the Northwind portal: Northwind Design marked Active with the customer as Owner, a members list with a role picker and a Remove button, an invite form with email and role, a pending invitation with Resend and Revoke buttons, and a Create an organization form.
Members, invitations and roles. What each person can do comes from their role's tier, so an admin cannot remove an owner.

The invitation link opens a page that says who sent it, for which organization and role, the masked address it went to and when it expires. Signed out, the invitee signs in or signs up and comes back to it. Signed in as a different address, the page names the address to use.

A Join an organization page in the Northwind portal saying a masked address invited the customer to join Northwind Design as Member, that the invitation was sent to a masked address and expires on Nov 17, 2026, with an Accept invitation button and a link to sign out and use another account.
The accept page, with both addresses masked.

Desktop apps

If your application binds sign-in to devices, a customer at the limit needed a release button in your app or a support ticket. The portal now has a Devices page that lists their active devices with when each was last used, and Remove signs that device out and frees its slot. Your desktop app can send a customer who hits the limit straight to /<slug>/login?return=devices, and they land back on the page after signing in. If you keep device release with your support team (deviceRelease: server_only), the page asks them to contact support instead.

The Devices page of the Northwind portal, explaining that the plan allows 2 devices and both are in use, with a See plans link, and two devices, Travel laptop and Studio iMac, each with its last-used date and a Remove button.
A customer at a two-device limit, with the next step spelled out.

The portal is also the sign-in and consent page for an app that opens the system browser to sign in. If your application acts as an OpenID Connect provider or a hosted MCP server and you have not set your own hostedAuthorizeUrl, the authorization endpoint now sends the browser to the portal. The customer signs in with whatever the portal offers, sees which app is asking, where they will be sent and what it can read, and chooses Allow or Cancel. Consent is asked every time.

An Allow access to your Northwind account page in the portal, naming Northwind Desktop as the app requesting access, noting that apps choose their own names, showing the destination 127.0.0.1:53682 described as a program on this computer, listing three things the app can see, the signed-in address, a Not you button, and Allow and Cancel buttons.
Consent for a desktop app. The page warns that app names are not checked and shows where the answer goes.
A You are signed in page in the portal, saying the customer can return to Northwind Desktop and close the tab, with an Open Northwind Desktop link in case the app did not open.
After Allow, the portal hands the browser back to the app and keeps a link in case it did not open.

Browser sign-in does not work for an application that binds sign-in to a device (deviceBinding: 'required'). The portal's own session holds no device slot, so it cannot approve another app. The page says so and sends the app access_denied. Such an application needs its own authorization page, set as hostedAuthorizeUrl, that calls /grant from your server with your secret key and a normal device-bound session. The details are in docs/oidc-provider.md and docs/devices.md.

Your own domain

The portal can answer on a hostname of yours, such as billing.yourapp.com, with no slug in its URLs. The order matters, because verifying switches your emailed links, the authorization page and checkout returns to the new domain straight away:

  1. Enter the hostname under Application, Billing, Portal, Custom domain, and save.
  2. Add a CNAME from it to your portal host.
  3. Publish the TXT record the panel shows, named _rekey-portal.<hostname>.
  4. Route the host to the portal over HTTPS. Until it is verified, the portal answers it with an empty 404, which is how you can tell the routing and certificate work.
  5. Press Verify domain.
The Custom domain card in the Rekey panel for billing.northwind.example, status Pending verification, with three steps: a CNAME record to point the domain at the portal, a TXT record to prove control with copy buttons, and a Verify domain button with a note to verify only after the domain reaches the portal over HTTPS.
The panel lists the records with copy buttons and warns you to verify only once HTTPS works.

Rekey checks the TXT record again about every six hours. After three misses in a row the domain lapses and links go back to the shared address, so leave the record in place. Self-hosted with docker-compose.prod.yml, list your domains in PORTAL_EXTRA_HOST_RULES and Traefik routes them and gets a certificate for each. Behind another proxy, route the host to the portal and give it a certificate yourself. The full steps are in docs/portal.md.

It looks like your product

The portal carries your name, logo and the primary, background and surface colours from your portal branding, and links your terms, privacy policy and support contact in the footer. A colour that would leave the portal's text hard to read falls back to the default. With no brand colours set, the portal follows the visitor's dark mode. Touch targets are at least 44 pixels, and dates and amounts are written in the visitor's locale.

The Northwind portal overview in dark mode, with the same subscription, plan, usage and plans cards on a near-black background.The Northwind portal overview on a phone, with the tabs, the subscription card and the plan and usage cards stacked in one column.
The same overview in dark mode, and on a phone.

Required two-factor is now enforced

When an application sets mfa: 'required', 2.4 checks it on every session, not only at sign-in. A user with no confirmed authenticator, including one who signs in only with a passkey, gets 403 MFA_ENROLLMENT_REQUIRED on ordinary user routes. Sessions issued before the upgrade are held back the same way.

Such a user can still enrol and sign out: GET /auth/mfa/status, POST /auth/mfa/setup, POST /auth/mfa/setup-confirm, POST /auth/send-verification, POST /auth/sign-out-everywhere, and GET /auth/me and GET /users/me without include. If your own app signs users in, check for mfaEnrollmentRequired on the sign-in result and send those users through setup before anything else. Treat a MFA_ENROLLMENT_REQUIRED from any other call the same way. Customers who use the portal are taken to its setup page automatically.

Upgrading

One migration, 20261110132835_portal_domain_verification. Run prisma migrate deploy, then deploy the API, panel and portal together, because the portal config response changed shape. Upgrade the @rekey.dev/* packages at the same time. The rest of what needs action:

  • For a device-bound application, set the same INTERNAL_CALLER_SECRET on the API and the portal, or its customers still get DEVICE_FINGERPRINT_REQUIRED in the portal. The portal's PORTAL_BASE_URL must have the same origin as the API's PUBLIC_PORTAL_URL.
  • An application with oidcEnabled or mcpEnabled, the portal on and no hostedAuthorizeUrl now sends authorization to the portal instead of the built-in page.
  • Organization invitations are emailed on invite and on resend. If your app mailed the link itself, stop, or the invitee gets two.
  • billing.getPlans in @rekey.dev/node and getPlans in @rekey.dev/react return PublicPlanPage, which adds defaultPlanSlug.
  • Deleting an account with no password and no two-factor from a browser now answers 401 EMAIL_CODE_REQUIRED. Request a code with POST /auth/email-code and send it as emailCode.
  • Portal brand colours must be 3 or 6 digit hex. Anything else falls back to the default.
  • Rotating JWT_SECRET lapses every verified custom domain until its TXT record is published again.

The steps in order, with the commands and checks, are in upgrading a self-hosted Rekey to 2.4.0. The full list, including the new endpoints, SDK methods and error codes, is in the 2.4.0 changelog.

What is not in 2.4

The portal does not yet let a customer update their payment method, download invoices or change plan in place. Subscribing to another plan starts a second subscription and cancels nothing, and the plan page says so before they pay. Passkeys are not managed in the portal either. These are on the changelog as planned, without dates.

Where to go next

  1. Read what customers can do page by page in docs/portal.md.
  2. Let a desktop or MCP client sign users in with your application as an OpenID Connect provider.
  3. Decide whether you need teams with when to use organizations.

Was this page helpful?

What's new in Rekey 2.4 | Rekey