W WRNexusJS
Tooling · Package reference

@wrnexus/helpers

Safe Context URL helpers and forward-auth login redirects.

v0.8.7Private registryTooling

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/helpers@0.8.7

Request preview access. Never put registry tokens in source control.

Safe convenience helpers for common WRNexusJS application flows. The package uses standard Context, URL, and Response values and has no runtime dependency beyond @wrnexus/core.

bun add @wrnexus/helpers

The package is private, so the machine must be authenticated to the wrnexus npm organization.

Usage

Redirect an unauthenticated forward-auth request

The gateway calls an SSO verifier on a different URL from the original application. These helpers reconstruct the original URL from the gateway headers and safely place it in the login redirect:

import type { Context } from "@wrnexus/core";
import { redirectToLogin } from "@wrnexus/helpers";

export const GET = async (ctx: Context) => {
  if (await hasValidSession(ctx)) {
    return new Response(null, { status: 204 });
  }

  return redirectToLogin(ctx, "/login", {
    allowedHosts: ["admin.localhost:3000", "reports.localhost:3000"],
  });
};

This creates a response such as:

Location: http://sso.localhost:3000/login?returnTo=http%3A%2F%2Fadmin.localhost%3A3000%2F

Always list the application hosts that are valid redirect destinations. Forwarded host headers are rejected when allowedHosts is absent or does not match, preventing an open redirect.

The SSO hostname is the login destination, not an allowedHosts entry. For example, when protecting admin.localhost:3000, keep admin.localhost:3000 in the allowlist even though the verifier runs at sso.localhost:3000. WRNexus preserves both hosts across a nested gateway request.

Support dynamic tenant domains

import type { Context } from "@wrnexus/core";
import { getOriginalRequestOrigin, redirectToLogin } from "@wrnexus/helpers";

export const GET = async (ctx: Context) => {
  const allowedHosts = (host: string) => host === "example.test" || host.endsWith(".example.test");

  console.info("Authentication requested by", getOriginalRequestOrigin(ctx, { allowedHosts }));
  return redirectToLogin(ctx, "https://auth.example.test/login", {
    allowedHosts,
    returnToParam: "continue",
    status: 303,
  });
};

API

  • getOriginalRequestUrl(ctx, options): URL — reconstruct the gateway URL.
  • getOriginalRequestOrigin(ctx, options): string — return only its origin.
  • getOriginalRequestPath(ctx): string — return its path and query string.
  • getOriginalRequestMethod(ctx): string — return its HTTP method.
  • redirectToLogin(ctx, loginUrl, options): Response — create a login redirect with an
  • encoded returnTo parameter.

For direct requests without gateway headers, URL helpers use ctx.url.

Complete TypeScript API

Generated from the exact installed package declarations.

import { Context } from '@wrnexus/core';

declare function appOrigin(appName: string): string;
declare function appUrl(appName: string, path?: string): string;
declare function currentAppName(): string | undefined;
declare function currentAppOrigin(): string | undefined;
declare function workspaceAppOrigins(): Readonly<Record<string, string>>;
/** Shared DNS suffix for configured workspace apps (for example `staging.example.com`). */
declare function workspaceRootDomain(): string;

interface RetryOptions {
    attempts?: number;
    minDelayMs?: number;
    maxDelayMs?: number;
    factor?: number;
    jitter?: number;
    signal?: AbortSignal;
    retryIf?: (error: unknown, attempt: number) => boolean | Promise<boolean>;
    onRetry?: (error: unknown, attempt: number, delayMs: number) => void | Promise<void>;
}
declare function backoffDelay(attempt: number, options?: Pick<RetryOptions, "minDelayMs" | "maxDelayMs" | "factor" | "jitter">): number;
declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
declare function retry<T>(operation: (attempt: number, signal?: AbortSignal) => Promise<T>, options?: RetryOptions): Promise<T>;
declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message?: string, signal?: AbortSignal): Promise<T>;
declare function stableStringify(value: unknown): string;
declare function safeJsonParse<T>(value: string, fallback: T): T;
declare function clamp(value: number, min: number, max: number): number;
declare function once<T extends (...args: any[]) => any>(fn: T): T;

/**
 * @wrnexus/helpers — safe conveniences for common WRNexusJS application flows.
 *
 * Helpers stay small and composable. They accept the standard WRNexusJS Context
 * and return web-platform values such as URL and Response.
 */

type RequestContext = Pick<Context, "req" | "url">;
type AllowedHosts = readonly string[] | ReadonlySet<string> | ((host: string, ctx: RequestContext) => boolean);
interface OriginalRequestOptions {
    /**
     * Hosts that the application permits as redirect destinations. This is
     * required when a proxy supplied X-Forwarded-Host is present.
     */
    allowedHosts?: AllowedHosts;
}
interface LoginRedirectOptions extends OriginalRequestOptions {
    /** Query parameter that receives the original absolute URL. */
    returnToParam?: string;
    /** Browser redirect status. Defaults to 302. */
    status?: 301 | 302 | 303 | 307 | 308;
}
/** Get the original path and query string seen by the gateway. */
declare function getOriginalRequestPath(ctx: RequestContext): string;
/** Get the original HTTP method seen by the gateway. */
declare function getOriginalRequestMethod(ctx: RequestContext): string;
/**
 * Reconstruct the absolute URL that reached the gateway.
 *
 * Forwarded hosts are never trusted implicitly: pass allowedHosts when this is
 * used behind the WRNexusJS gateway. Direct requests fall back to ctx.url.
 */
declare function getOriginalRequestUrl(ctx: RequestContext, options?: OriginalRequestOptions): URL;
/** Get the original request origin, for example http://admin.localhost:3000. */
declare function getOriginalRequestOrigin(ctx: RequestContext, options?: OriginalRequestOptions): string;
/**
 * Redirect to a login page with the original absolute URL encoded as returnTo.
 * Relative login URLs resolve against the current app (normally the SSO app).
 */
declare function redirectToLogin(ctx: RequestContext, loginUrl: string | URL, options?: LoginRedirectOptions): Response;

export { type AllowedHosts, type LoginRedirectOptions, type OriginalRequestOptions, type RequestContext, type RetryOptions, appOrigin, appUrl, backoffDelay, clamp, currentAppName, currentAppOrigin, getOriginalRequestMethod, getOriginalRequestOrigin, getOriginalRequestPath, getOriginalRequestUrl, once, redirectToLogin, retry, safeJsonParse, sleep, stableStringify, withTimeout, workspaceAppOrigins, workspaceRootDomain };

Examples

Copy-ready examples from the installed package documentation.

Redirect an unauthenticated forward-auth request

import type { Context } from "@wrnexus/core";
import { redirectToLogin } from "@wrnexus/helpers";

export const GET = async (ctx: Context) => {
  if (await hasValidSession(ctx)) {
    return new Response(null, { status: 204 });
  }

  return redirectToLogin(ctx, "/login", {
    allowedHosts: ["admin.localhost:3000", "reports.localhost:3000"],
  });
};

Support dynamic tenant domains

import type { Context } from "@wrnexus/core";
import { getOriginalRequestOrigin, redirectToLogin } from "@wrnexus/helpers";

export const GET = async (ctx: Context) => {
  const allowedHosts = (host: string) => host === "example.test" || host.endsWith(".example.test");

  console.info("Authentication requested by", getOriginalRequestOrigin(ctx, { allowedHosts }));
  return redirectToLogin(ctx, "https://auth.example.test/login", {
    allowedHosts,
    returnToParam: "continue",
    status: 303,
  });
};