# @wrnexus/helpers

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

## Installation

```bash
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:

```ts
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:

```text
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

```ts
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`.
