Give a Hraness product one registered sign-in and authorization boundary without
letting application code choose what to trust. @hraness/suite-accounts
accepts an exact product binding, returns closed OAuth and OIDC configuration,
keeps browser bearer custody on the server, and parses identity and entitlement
evidence from unknown.
The first useful result is a frozen configuration tied to one known origin, callback, client ID, and authentication mode. A misspelled field, retired client, unregistered origin, or caller-supplied trust value fails before the product starts an Accounts flow.
Accounts remains the sole authority for account records, OAuth client registration, identity links, and entitlements. Installing this package does not let a product register itself or choose an issuer, JWKS endpoint, resource, callback, client ID, wire version, or trust algorithm.
Pin the immutable release:
{
"dependencies": {
"@hraness/suite-accounts": "github:hraness/suite-accounts#v0.5.2"
}
}Then install with Bun 1.3.14:
bun installReact and React DOM 18.3.1 through 19.x are optional peers. Install them only
when using @hraness/suite-accounts/react or
@hraness/suite-accounts/profile-form.
Import @hraness/suite-accounts/profile-form.css once when rendering the profile
form. It loads the precompiled StyleX stylesheet; consumers do not need a Babel
plugin or a StyleX runtime compiler. The semantic suite-profile-* classes stay
available for inspection, and caller classes remain on the form.
The seven public variables are --suite-profile-input-background,
--suite-profile-line, --suite-profile-focus, --suite-profile-muted,
--suite-profile-button-background, --suite-profile-button-foreground, and
--suite-profile-error. Native readonly, disabled, and focus-visible behavior
is preserved. Width and padding retain the existing physical-axis contract in
vertical writing modes.
Applications that already join StyleX package rules can consume
@hraness/suite-accounts/stylex-manifest.json and
@hraness/suite-accounts/compiler-foundation.css with the public
@hraness/ui/stylex-build compiler. In that mode, include the generated rule
union instead of the standalone profile-form.css or stylex.css export.
The profile form is the only styled runtime entry. Root authentication and
protocol imports do not import React or StyleX presentation.
Version 0.5.2 builds the profile manifest with UI v0.5.12 and its fail-fast property-validation contract. Compiler adopters must use compatible manifests for every registered package and start a fresh generation after upgrading. The prior rollback pair is Suite Accounts v0.5.1 with UI v0.5.3. This compiler change adds no runtime or peer dependency and does not change authentication, profile behavior, or the existing presentation recipes.
Pass the exact public fields assigned to the product. The factory rejects unknown fields, including attempts to supply authority-controlled trust data.
import { createSuiteAccountsClientConfiguration } from
"@hraness/suite-accounts/client-configuration";
const configuration = createSuiteAccountsClientConfiguration({
authMode: "oidc-rp",
callbackUrl: "https://hra.sh/api/suite-auth/callback",
clientId: "hraness:hra:production:v1",
consumer: "hra",
environment: "production",
origin: "https://hra.sh",
});
if (!configuration.ok) {
throw new Error(`Invalid Accounts binding: ${configuration.error}`);
}
configuration.value.provider.issuer;
// "https://account.hraness.com"For the registered HRA production client, the checked result begins with:
{
"authBasePath": "/api/suite-auth",
"configurationVersion": "suite-accounts-client-configuration-v1",
"provider": {
"issuer": "https://account.hraness.com",
"resource": "https://hraness.com/suite"
},
"wireVersion": "v1"
}The example shows selected fields from the returned configuration. The package also supplies the authorization, token, revocation, userinfo, JWKS, identity link, and entitlement receipt endpoints from closed current-authority data.
The returned configuration is frozen. Its provider endpoints, resource, configuration version, and wire version are derived from the package's checked current authority data. The Accounts service independently enforces the same registration, so this client-side check never creates authority.
Local development still uses parseSuiteAccountsPublicConfig. The consumer
origin and both Accounts Convex origins must use one exact loopback hostname.
Remote configuration accepts only the checked production deployment.
Generated Vercel Preview surfaces can report their surface origin through
NEXT_PUBLIC_VERCEL_SURFACE_ORIGIN, but suite authentication remains
unavailable there.
browser
-> product-owned /api/suite-auth route and encrypted HttpOnly session
-> registered Accounts authorization, token, userinfo, and JWKS endpoints
-> product server verifies audience, client binding, receipt, and entitlement
-> browser receives bounded session JSON, never an OAuth bearer
The package participates at each protocol boundary, but it does not become the Accounts service or the product backend:
| Authority | Owns |
|---|---|
| Accounts service | Account records, client registration, identity links, entitlements, and authoritative provider operations |
@hraness/suite-accounts |
Closed registrations, trust configuration, protocol parsers, server transports, receipt verification, and optional React adapters |
| Product | Its registered origin and callback, cookie encryption keys, local subject, authorization decision, interface copy, and provider deployment |
Import provider-neutral identity parsers and signed-message builders from the
identity subpath:
import {
parseSuiteAccountId,
parseSuiteJwtClaims,
parseSuiteUsername,
suiteEntitlementReceiptMessage,
} from "@hraness/suite-accounts/identity";The identity catalog contains only the finite revisions, plan IDs, feature IDs, and plan-to-feature relationship required to verify signed evidence. Prices, provider lookup keys, product copy, and provider event replay policy are not part of this package.
parseSuiteJwtClaims performs structural parsing. It does not establish
signature, issuer, audience, or time trust. Use the registry-pinned verifier
for authorization.
| Subpath | Intended runtime |
|---|---|
. |
Dependency-light configuration, registry, URL, and Convex browser-token contracts |
./auth-client |
Legacy product-neutral browser auth adapter |
./identity |
Provider-neutral identity types, parsers, views, and signed messages |
./identity/functions |
Opt-in Convex function references and billing transport types |
./identity/return-targets |
Opt-in Accounts-owned billing return-target identifiers |
./client-configuration |
Additive validated product-binding factory |
./browser-session |
Same-origin browser session reads and serialized refresh |
./oidc-rp |
Server-only OAuth 2.1 relying-party implementation |
./oidc-session-policy |
Shared encrypted-cookie and session-lifetime policy |
./oidc-surface-server |
Registered browser-RP server handlers |
./bearer-verifier |
Server-only registry-pinned ES256 bearer verification |
./receipt-verifier |
Server-only HMAC receipt and product-keyring verification |
./convex-browser-auth |
Product Convex token configuration and identity parsing |
./convex-browser-auth-browser |
Memory-only browser bearer loader |
./convex-browser-auth-server |
Server-only short-token signing and JWKS handlers |
./auth-proxy |
Same-origin compatibility proxy with fixed headers and cookies |
./entitlements |
Post-signature entitlement checks and receipt ordering |
./profile |
Provider-neutral profile contracts |
./profile-form |
Optional controlled React profile editor |
./profile-form.css |
Product-neutral profile-form styles |
./stylex.css |
Precompiled standalone profile rules |
./stylex-manifest.json |
Canonical rule manifest for an application-wide compiler join |
./compiler-foundation.css |
Empty structural foundation for compiler adopters |
./public-config |
Validated public development and production configuration |
./react |
Optional route-local Accounts Convex context |
./registry |
Deprecated v1 compatibility registry and distinct current authority |
./urls |
Closed Accounts and product URL helpers |
Import server-only modules only from server code.
The package preserves these checks across the public surface:
- OAuth authorization code uses S256 PKCE plus exact state and nonce checks.
- Issuer, audience, origin, callback, client ID, resource, and endpoints must match the registered values.
- OAuth access and refresh tokens remain in encrypted HttpOnly cookies or server-to-server requests. Browser session JSON never exposes bearer tokens.
- A verified callback ends the cross-site redirect chain before a nonce-locked continuation starts the product navigation from its registered origin.
- Refresh-token rotation uses an origin-scoped exclusive lock and re-reads the session after acquiring it.
- Bearer verification accepts only bounded public P-256 ES256 keys from the pinned JWKS endpoint and enforces both client-binding claims.
- Product backends grant access only after receipt, replay, freshness, entitlement, and exact feature checks succeed.
- Missing legacy entitlement claims grant no suite features. Malformed claims invalidate the token.
Do not authorize from browser profile JSON, decoded JWT data, discovery destinations, billing plan membership, or an unverified receipt.
SUITE_CONSUMER_IDS preserves released identity values for historical parsing.
SUITE_ACCOUNTS_CONSUMERS, SUITE_ACCOUNTS_DEPLOYMENTS, their policy arrays,
and their lookup helpers are deeply runtime-frozen and deprecated for new
consumers. Retired identities are absent from those registration and trust
surfaces even when a parser still accepts them at a bounded historical
boundary.
Current authority lives under the distinct
SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS and
SUITE_ACCOUNTS_CURRENT_CONSUMERS exports and their current lookup helpers.
New registration remains an Accounts service change followed by a package
release and an exact factory binding. Retired client identifiers and routes are
rejected by current APIs.
The published v1 browser refresh-lock and session-notification channel strings also remain unchanged in version 0.1. Existing tabs therefore coordinate across a rolling package migration without a browser namespace cutover.
The compatibility registry is intentionally closed. It must not gain runtime mutation, remote discovery, environment overrides, or caller-supplied trust values.
The immutable v0.5.2 release matches the install example and package
manifest. Its current changes remain bounded:
| Release | Checked change |
|---|---|
v0.5.2 |
Rebuilds the unchanged profile recipes and manifest against UI v0.5.12, binding fail-fast property validation while preserving presentation-free authentication entries and optional React peers. |
v0.5.1 |
Ends the verified OAuth callback with a nonce-locked same-origin continuation document, so the return page resolves its session without admitting a cross-site request. |
v0.5.0 |
Compiles the optional native profile form with StyleX, preserving public variables, semantic hooks, save behavior, and non-React authentication boundaries. Publishes a canonical compiler manifest and verifies standalone styles in a real browser. |
v0.4.2 |
Adds verified-account-email accessors for provisioning before optional username onboarding. Live userinfo must match subject, client, Suite account, and profile state; the accessor returns only an email_verified address. |
v0.4.1 |
Registers PeopleBlade at https://peopleblade.com for email-OTP OIDC. Its signed product-link receipt binds local and Suite subjects; email equality never creates or merges a link. |
v0.4.0 |
Removes the retired OPRTE browser client from current and deprecated registration helpers while preserving bounded historical product-ID parsing. |
v0.3.7 |
Moves the stable slackorgs consumer registration to BigDataDepot at https://bigdatadepot.com without changing its client ID. Predecessor origins gain no Accounts authority. |
v0.3.6 |
Moves the current Sponge origin to https://sponge.computer without changing its client ID. |
Deterministic tests exercise valid registrations and readable failures. Property tests cover foreign-value parsers, ordering, and round trips. The package smoke installs built entries into clean Bundler and NodeNext consumers with React 18.3.1 and 19.2.3, then builds the client entries in a clean Next.js 16.2 webpack consumer.
This repository does not contain billing prices or provider lookup keys, authoritative registry writes, reconciliation jobs, email delivery, provider credentials, webhook payloads, provider project identifiers, or product-owned service policy. Those concerns belong to the Accounts service. The Convex function references and billing transport types required by existing clients are isolated behind explicit opt-in identity subpaths; the root and client configuration entries do not load them.
No. The product must already exist in current Accounts authority. The validated factory can confirm that binding, but it cannot create or widen it.
No. Browser JSON is a display and session-state surface. A product server must verify signature, issuer, audience, client binding, time, receipt ordering, and the exact required entitlement before it grants access.
No. It contains finite identity and entitlement protocol values plus opt-in transport types. Prices, provider keys, events, credentials, and product policy stay with their authoritative services.
Start with ./client-configuration and commit the exact assigned binding. Add
./oidc-surface-server for registered browser sign-in, then import only the
server verifiers and identity subpaths required by the product. Run the package
and product gates before the callback is enabled in production.
bun install --frozen-lockfile --ignore-scripts
bun run check
bun run test:browser
bun pm pack --dry-run --ignore-scriptsbun run check validates the portable portfolio inventory, runs independent
ESLint and TypeScript configuration, executes deterministic and property
tests, builds the ESM entries, verifies canonical StyleX rules and deterministic
artifacts across isolated roots, scans every source and packed file, and installs the
package in clean Bundler and NodeNext consumers on React 18.3.1 and 19.2.3. It
also builds the packed React entries in a clean Next.js 16.2 webpack consumer,
which verifies that every client entry has one valid top-level directive.
bun run test:browser uses the built profile form and OIDC continuation under
strict Content Security Policies. It checks server-rendered light and dark
styles at compact and wide widths, native focus and readonly controls, vertical
writing, hydration, pending saves, conflict revisions, validation errors, and
save failures. It also verifies that a real cross-site callback ends before the
nonce-locked continuation starts a same-origin session request. Only the save
transport and callback provider are synthetic. The script uses an installed
browser without downloading one; set CHROMIUM_EXECUTABLE_PATH to select its
executable. It prints the retained temporary profile evidence directory and
closes its own browsers and loopback servers. Branch and release verification
both require this browser check.
Read CONTRIBUTING.md before opening a pull request. Report suspected vulnerabilities privately as described in SECURITY.md.
MIT