Awwwth Docs

Awwwth / Install in your app

CLI installation and backend integration

Documentation index · Shared sign-in · Publishing

The public awwwth npm package adds an authentication gateway to an existing HTTP backend. The gateway runs on Node.js 22+ alongside your application, so the application itself can use any backend language or framework. It does not rewrite framework routes or install framework dependencies.

Requirements

  • Node.js 22+ and npm on the application host.
  • A running Awwwth identity server with the shared-sign-in changes deployed.
  • A registered application with a client ID, client secret, and exact callback URL.
  • An HTTP backend reachable privately from the gateway.
  • HTTPS for production public origins; loopback HTTP is allowed for development.

The CLI cannot register trusted applications anonymously. Use the Awwwth account interface to register a company application as its owner, or a platform application as an administrator. For API-based registration, see the API reference.

Install in an existing project

For local development, choose these addresses:

ComponentAddress
Identity serverhttp://localhost:4100
Public application gatewayhttp://localhost:3000
Private application backendhttp://127.0.0.1:3001
Registered callbackhttp://localhost:3000/auth/awwwth/callback

Register that callback in Awwwth, save the returned client secret, and run this inside your application directory:

npx awwwth \
  --issuer http://localhost:4100 \
  --app-origin http://localhost:3000 \
  --upstream http://127.0.0.1:3001 \
  --client-id app_REPLACE_WITH_YOUR_ID

Enter the client secret at the hidden prompt. With no arguments, npx awwwth prompts for the issuer and credentials and uses the application/backend defaults above.

Start your existing backend at its private address using your framework's own command. In another terminal, start the generated gateway:

node --env-file=.env.awwwth .awwwth/server.mjs

If your project has a package.json, the installer also adds:

npm run awwwth

Open the gateway URL, http://localhost:3000, in your browser. It starts sign-in automatically when no valid app session exists. Complete account verification if required.

The gateway owns the app's entry points for account access. Link sign-in, sign-up, and password-help buttons to /auth/awwwth/login, /auth/awwwth/signup, and /auth/awwwth/forgot. Common GET routes /login, /sign-in, /signup, /sign-up, /register, /forgot, and /forgot-password also open the hosted Awwwth screens. For example, /auth/awwwth/signup?returnTo=/checkout returns to checkout after the new account is verified and authorized. Only local returnTo paths are accepted. The hosted flow also handles MFA and passkeys. Your application should stop submitting credentials to its old login and registration APIs and use the verified sub header for its own permissions and account records.

Files and changes

FileBehavior
.awwwth/server.mjsAdds the standalone HTTP gateway
.awwwth/config.mjsAdds environment validation
.awwwth/oidc.mjsAdds ID-token verification and cookie encryption
.awwwth/widget.mjs, .awwwth/widget.cssAdds the reusable modal and styles
.env.awwwthStores local credentials and a generated session encryption key; file mode 0600
.env.awwwth.exampleAdds a configuration template with secret values omitted
AWWWTH.mdAdds application-specific setup and usage instructions
.gitignoreAppends a rule excluding .env.awwwth
package.jsonAdds an awwwth script if a manifest already exists

Existing application code and existing scripts are preserved. A conflicting generated file, existing .awwwth directory, or existing awwwth script stops installation before changes are made. Non-Node projects do not receive a new package.json; use the standalone Node command.

The generated runtime is copied into your project. Publishing a newer npm version does not automatically update existing copies. Review upgrades deliberately and preserve the app's session secret unless you intend to invalidate its cookies.

Use the same modal in any frontend

Add this to a shared page layout, then use the buttons wherever account access belongs in the interface:

<script src="/auth/awwwth/widget.js" defer></script>
<button type="button" data-awwwth="login">Sign in</button>
<button type="button" data-awwwth="signup">Create account</button>
<button type="button" data-awwwth="forgot">Forgot password?</button>

The widget adapts to small screens, light/dark color schemes, keyboard input, and reduced-motion settings. Override --awwwth-accent, --awwwth-surface, --awwwth-text, --awwwth-muted, or --awwwth-border on the page to match an app's design. A button can set data-awwwth-return-to="/checkout" for a local post-login destination. The actual account form opens in a secure popup on the Awwwth origin; the overlay shows progress and errors. Awwwth cannot be embedded in an iframe because its account cookies, passkeys, and frame policy require its own origin.

For framework code, call window.Awwwth.open({ screen: 'login', reload: false }) from a user click, await the returned user, and update the app's auth state. window.Awwwth.session() reads the current user, and window.Awwwth.logout() revokes this app's session. The widget dispatches awwwth:authenticated when sign-in succeeds. The default behavior reloads the page so ordinary server-rendered apps pick up the new session automatically.

CLI options

OptionPurpose
--issuer URLCanonical Awwwth browser origin
--api-origin URLSeparate OAuth API origin, if the issuer advertises one
--app-origin URLPublic app origin; default http://localhost:3000
--upstream URLPrivate backend; default http://127.0.0.1:3001
--client-id IDRegistered app ID
--directory PATHTarget project; default current directory
--yesDisable prompts; provide credentials through environment/configuration arguments
--dry-runShow the files that would change without writing them
--helpPrint command help

The command also accepts npx awwwth init. For noninteractive use, provide AWWWTH_CLIENT_SECRET through a secure environment mechanism. AWWWTH_ISSUER, AWWWTH_API_ORIGIN, and AWWWTH_CLIENT_ID can also supply their corresponding values. Secrets are never accepted as command-line arguments.

Gateway environment

These settings belong to the consuming application, not the identity server's .env:

VariableMeaning
AWWWTH_ISSUERCanonical identity-server origin, without a trailing slash
AWWWTH_API_ORIGINOAuth API origin; defaults to issuer
AWWWTH_APP_ORIGINCanonical public application origin
AWWWTH_UPSTREAMPrivate HTTP/HTTPS backend origin, without credentials or a path
AWWWTH_CLIENT_IDThis app's registered client ID
AWWWTH_CLIENT_SECRETThis app's private client secret
AWWWTH_SESSION_SECRETCanonical base64url encoding of 32 random bytes; generated by the CLI
AWWWTH_HOSTGateway listening interface; default 127.0.0.1
AWWWTH_PORTListening port; defaults to the app origin's explicit port, otherwise 3000

Use different client IDs and session secrets for different apps. Replicas of the same app need the same stable session secret. Keep credentials on the server and out of browser bundles and version control.

Read the authenticated user

The gateway replaces caller-supplied identity headers with verified values:

HeaderValue
X-Awwwth-SubStable user UUID
X-Awwwth-UserBase64url-encoded JSON with sub, name, email, email_verified, and optional tenant_id

In a Node HTTP backend, decode the user from a request that arrived through the gateway:

const encodedUser = request.headers['x-awwwth-user'];
if (typeof encodedUser !== 'string') {
  response.writeHead(401);
  response.end('Authentication required');
  return;
}
const user = JSON.parse(Buffer.from(encodedUser, 'base64url').toString('utf8'));

Other languages can decode the same header using their base64url and JSON libraries. Only trust these headers when the backend is inaccessible directly from the public network. Use sub for user-owned resource checks; authentication does not replace your application's authorization rules.

Frontend code can read the user without handling OAuth tokens:

export async function currentUser() {
  const response = await fetch('/auth/awwwth/session', {
    credentials: 'same-origin',
    headers: { Accept: 'application/json' },
  });
  if (response.status === 401) {
    const returnTo = location.pathname + location.search;
    location.assign(`/auth/awwwth/login?${new URLSearchParams({ returnTo })}`);
    return null;
  }
  if (!response.ok) throw new Error('Sign-in is temporarily unavailable. Please retry.');
  const result = await response.json();
  return result.user;
}

Show a loading state while this function runs, handle a thrown error with a retry action, and use textContent or framework text interpolation when rendering profile fields.

Gateway endpoints

Method and pathResult
GET /auth/awwwth/loginStarts authorization; accepts an optional local returnTo path
GET /auth/awwwth/signupOpens Awwwth-hosted account creation for this app's directory
GET /auth/awwwth/forgotOpens Awwwth-hosted password recovery
GET /auth/awwwth/widget.jsServes the first-party frontend widget
GET /auth/awwwth/widget.cssServes its responsive styles
GET /auth/awwwth/callbackVerifies callback state and issuer, exchanges the code, validates the ID token, and sets the app cookie
GET /auth/awwwth/sessionReturns {user, login} with 200, or {user: null, login} with 401
POST /auth/awwwth/logoutRevokes this app's grant and clears its cookie; returns an account-site URL

All other upstream routes require authentication. A GET accepting text/html redirects to sign-in when needed; API requests receive 401 with a login URL. Browser navigation must perform the redirect; fetching the cross-origin authorization endpoint through AJAX is not the login flow.

App mutations require an Origin header exactly matching AWWWTH_APP_ORIGIN. OAuth credentials and tokens are not returned in the user endpoint or identity headers. See shared sign-in for expiry, revocation, and logout behavior.

Production deployment

  1. Deploy the updated identity server and register the app's production callback.
  2. Run the application's production backend on a private interface or network.
  3. Run the generated gateway as a separately supervised Node process.
  4. Configure public HTTPS to forward to the gateway and preserve the public app Host header.
  5. Supply production environment values and keep the same session secret across app replicas.
  6. Verify sign-in through the public URL and confirm direct public backend access is blocked.

For example, with AWWWTH_APP_ORIGIN=https://app.example.com, the gateway defaults to listening on port 3000. Your HTTPS reverse proxy should forward to that listener; the backend could listen privately on port 3001. The gateway itself does not terminate TLS.

Backend responses are marked private, no-store. Every authenticated request introspects its access token, so identity-server availability and capacity affect application traffic.

Limits and troubleshooting

Symptom or limitationExplanation or action
Callback rejectedRegister the exact scheme, hostname, port, and /auth/awwwth/callback path
Identity endpoint mismatchSet AWWWTH_API_ORIGIN to the separate API origin advertised by discovery
invalid_hostReverse proxy must preserve the public app Host header
invalid_originMutations must originate from the configured public app origin
authentication_unavailable / 503Check issuer availability, client credentials, API origin, and token verification configuration
Backend unavailable / 502Start the backend and verify the private upstream address
Login repeats or wrong directory is usedUse the same issuer and directory for both apps and the same browser profile
API returns 401 after several minutesNavigate to login to renew via the central session; this version does not refresh tokens in the background
Setup refuses to overwrite filesReview the existing installation; the CLI intentionally preserves it
Hot reload / WebSockets failWebSocket upgrades are unsupported; use a production build
Webhooks or public pages need anonymous accessThis gateway has no public-route exception mechanism
Static-only hostingA Node gateway process and reachable HTTP backend are required
Long-lived upstream connection closesThe gateway applies a 30-second upstream idle timeout

For a walkthrough with two independently configured applications, continue to Shared sign-in.