Files
WRNexusJSDoc/app/pages/packages/helpers.wrn
T

137 lines
10 KiB
Plaintext

page wrnexushelpers {
seo {
title = "@wrnexus/helpers"
description = "Safe Context URL helpers and forward-auth login redirects."
}
view {
<div class="docs-shell">
<a class="skip-link" href="#main">Skip to content</a>
<header class="topbar">
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.2.24</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
</header>
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
<main class="page package-page">
<aside class="sidebar"><a href="/packages">← All packages</a><span class="category">Tooling</span><h2>@wrnexus/helpers</h2><p>Safe Context URL helpers and forward-auth login redirects.</p><span class="status status-beta">Private preview · 0.2.24</span><nav><a href="#access">Access</a><a href="#guide">Guide</a><a href="#api">Complete API</a></nav></aside>
<article id="main" class="documentation"><section class="doc-intro"><span class="eyebrow">Tooling · Preview</span><h1>@wrnexus/helpers</h1><p>Safe Context URL helpers and forward-auth login redirects.</p><section id="access" class="access-callout"><h2>Private registry access required</h2><p>This package is not available from the public npm registry. After WorkRoot approves access and supplies private registry instructions, install the release-aligned package:</p><pre><code>bun add @wrnexus/helpers@0.2.24</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide" class="prose"><p>Safe convenience helpers for common WRNexusJS application flows. The package uses standard <code>Context</code>, <code>URL</code>, and <code>Response</code> values and has no runtime dependency beyond <code>@wrnexus/core</code>.</p>
<pre data-language="bash"><code>bun add @wrnexus/helpers</code></pre>
<p>The package is private, so the machine must be authenticated to the <code>wrnexus</code> npm organization.</p>
<h3 id="usage">Usage</h3>
<h4 id="redirect-an-unauthenticated-forward-auth-request">Redirect an unauthenticated forward-auth request</h4>
<p>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:</p>
<pre data-language="ts"><code>import type &#123; Context &#125; from &quot;@wrnexus/core&quot;;
import &#123; redirectToLogin &#125; from &quot;@wrnexus/helpers&quot;;
export const GET = async (ctx: Context) =&gt; &#123;
if (await hasValidSession(ctx)) &#123;
return new Response(null, &#123; status: 204 &#125;);
&#125;
return redirectToLogin(ctx, &quot;/login&quot;, &#123;
allowedHosts: [&quot;admin.localhost:3000&quot;, &quot;reports.localhost:3000&quot;],
&#125;);
&#125;;</code></pre>
<p>This creates a response such as:</p>
<pre data-language="text"><code>Location: http://sso.localhost:3000/login?returnTo=http%3A%2F%2Fadmin.localhost%3A3000%2F</code></pre>
<p>Always list the application hosts that are valid redirect destinations. Forwarded host headers are rejected when <code>allowedHosts</code> is absent or does not match, preventing an open redirect.</p>
<p>The SSO hostname is the login destination, not an <code>allowedHosts</code> entry. For example, when protecting <code>admin.localhost:3000</code>, keep <code>admin.localhost:3000</code> in the allowlist even though the verifier runs at <code>sso.localhost:3000</code>. WRNexus preserves both hosts across a nested gateway request.</p>
<h4 id="support-dynamic-tenant-domains">Support dynamic tenant domains</h4>
<pre data-language="ts"><code>import type &#123; Context &#125; from &quot;@wrnexus/core&quot;;
import &#123; getOriginalRequestOrigin, redirectToLogin &#125; from &quot;@wrnexus/helpers&quot;;
export const GET = async (ctx: Context) =&gt; &#123;
const allowedHosts = (host: string) =&gt; host === &quot;example.test&quot; || host.endsWith(&quot;.example.test&quot;);
console.info(&quot;Authentication requested by&quot;, getOriginalRequestOrigin(ctx, &#123; allowedHosts &#125;));
return redirectToLogin(ctx, &quot;https://auth.example.test/login&quot;, &#123;
allowedHosts,
returnToParam: &quot;continue&quot;,
status: 303,
&#125;);
&#125;;</code></pre>
<h3 id="api">API</h3>
<ul>
<li><code>getOriginalRequestUrl(ctx, options): URL</code> — reconstruct the gateway URL.</li>
<li><code>getOriginalRequestOrigin(ctx, options): string</code> — return only its origin.</li>
<li><code>getOriginalRequestPath(ctx): string</code> — return its path and query string.</li>
<li><code>getOriginalRequestMethod(ctx): string</code> — return its HTTP method.</li>
<li><code>redirectToLogin(ctx, loginUrl, options): Response</code> — create a login redirect with an</li>
<p>encoded <code>returnTo</code> parameter.</p>
</ul>
<p>For direct requests without gateway headers, URL helpers use <code>ctx.url</code>.</p></section><section id="api" class="prose api"><h2>Complete TypeScript API</h2><p>This declaration comes from the exact installed package and lists its exported functions, classes, interfaces, and types.</p><pre data-language="typescript"><code>import &#123; Context &#125; from '@wrnexus/core';
/**
* @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&lt;Context, &quot;req&quot; | &quot;url&quot;&gt;;
type AllowedHosts = readonly string[] | ReadonlySet&lt;string&gt; | ((host: string, ctx: RequestContext) =&gt; boolean);
interface OriginalRequestOptions &#123;
/**
* Hosts that the application permits as redirect destinations. This is
* required when a proxy supplied X-Forwarded-Host is present.
*/
allowedHosts?: AllowedHosts;
&#125;
interface LoginRedirectOptions extends OriginalRequestOptions &#123;
/** Query parameter that receives the original absolute URL. */
returnToParam?: string;
/** Browser redirect status. Defaults to 302. */
status?: 301 | 302 | 303 | 307 | 308;
&#125;
/** 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 &#123; type AllowedHosts, type LoginRedirectOptions, type OriginalRequestOptions, type RequestContext, getOriginalRequestMethod, getOriginalRequestOrigin, getOriginalRequestPath, getOriginalRequestUrl, redirectToLogin &#125;;
</code></pre></section><section id="examples" class="prose examples"><h2>Examples</h2><p>Examples are taken from this package's installed documentation and must be evaluated with its requirements and stability notes.</p><div class="example-grid"><article class="example-card"><h3>Redirect an unauthenticated forward-auth request</h3><pre data-language="ts"><code>import type &#123; Context &#125; from &quot;@wrnexus/core&quot;;
import &#123; redirectToLogin &#125; from &quot;@wrnexus/helpers&quot;;
export const GET = async (ctx: Context) =&gt; &#123;
if (await hasValidSession(ctx)) &#123;
return new Response(null, &#123; status: 204 &#125;);
&#125;
return redirectToLogin(ctx, &quot;/login&quot;, &#123;
allowedHosts: [&quot;admin.localhost:3000&quot;, &quot;reports.localhost:3000&quot;],
&#125;);
&#125;;</code></pre></article><article class="example-card"><h3>Support dynamic tenant domains</h3><pre data-language="ts"><code>import type &#123; Context &#125; from &quot;@wrnexus/core&quot;;
import &#123; getOriginalRequestOrigin, redirectToLogin &#125; from &quot;@wrnexus/helpers&quot;;
export const GET = async (ctx: Context) =&gt; &#123;
const allowedHosts = (host: string) =&gt; host === &quot;example.test&quot; || host.endsWith(&quot;.example.test&quot;);
console.info(&quot;Authentication requested by&quot;, getOriginalRequestOrigin(ctx, &#123; allowedHosts &#125;));
return redirectToLogin(ctx, &quot;https://auth.example.test/login&quot;, &#123;
allowedHosts,
returnToParam: &quot;continue&quot;,
status: 303,
&#125;);
&#125;;</code></pre></article></div></section></article>
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#usage">Usage</a><a class="toc-level-4" href="#redirect-an-unauthenticated-forward-auth-request">Redirect an unauthenticated forward-auth request</a><a class="toc-level-4" href="#support-dynamic-tenant-domains">Support dynamic tenant domains</a><a class="toc-level-3" href="#api">API</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
</main>
<footer>WRNexusJS 0.2.24 · Private Developer Preview · Bun-native · Documentation generated from installed package APIs.</footer>
</div>
}
}