@relipay/nextjs 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,59 @@
1
+ # AGENTS.md — `@relipay/nextjs`
2
+
3
+ Next.js 14/15 App Router helpers built on top of `@relipay/node`.
4
+
5
+ ## Two entrypoints
6
+
7
+ | Import | Runtime | Use case |
8
+ |---|---|---|
9
+ | `@relipay/nextjs/middleware` | **Edge** | Wire into `middleware.ts` to gate routes |
10
+ | `@relipay/nextjs/server` | **Node** | `auth()`, `signIn()`, `signUp()`, `signOut()` for server components / server actions / route handlers |
11
+
12
+ The split exists because middleware runs in Edge and can't import `@prisma/client` etc. that `@relipay/node` pulls in transitively.
13
+
14
+ ## Required env
15
+
16
+ ```
17
+ RELIPAY_URL=https://your-relipay.example.com
18
+ RELIPAY_SECRET=rp_live_… # Application secret key — server-only
19
+ ```
20
+
21
+ ## Five-line happy path
22
+
23
+ ```ts
24
+ // middleware.ts
25
+ import { relipayMiddleware } from '@relipay/nextjs/middleware';
26
+ export default relipayMiddleware({ publicRoutes: ['/sign-in', '/sign-up'] });
27
+ export const config = { matcher: ['/((?!_next|.*\\..*).*)'] };
28
+
29
+ // app/dashboard/page.tsx
30
+ import { auth } from '@relipay/nextjs/server';
31
+ export default async function Dashboard() {
32
+ const session = await auth();
33
+ if (!session) return null; // middleware already redirected; defensive
34
+ return <p>Hi {session.user.email}</p>;
35
+ }
36
+
37
+ // app/sign-in/actions.ts
38
+ 'use server';
39
+ import { signIn } from '@relipay/nextjs/server';
40
+ export async function signInAction(fd: FormData) {
41
+ await signIn({ email: String(fd.get('email')), password: String(fd.get('password')) });
42
+ redirect('/dashboard');
43
+ }
44
+ ```
45
+
46
+ ## Cookie model
47
+
48
+ Two httpOnly cookies, set by `signIn` / `signUp`:
49
+
50
+ - `relipay_access` — 15 min, the user's JWT
51
+ - `relipay_refresh` — 30 days, the refresh token
52
+
53
+ `auth()` tries access first, falls back to refresh-and-rotate, returns `null` only when both fail. Auto-rotation in `auth()` works when called from a server action or a route handler (Next.js limitation: server components can't write cookies, so a stale-access read in a server component returns null instead of refreshing — handle this in your protected page by also calling `auth()` inside the action that triggered the page).
54
+
55
+ ## What NOT to do
56
+
57
+ - Don't import `@relipay/nextjs/server` from a client component or a middleware. It pulls Node-only deps; bundling will fail or balloon.
58
+ - Don't bypass the cookies (e.g. read `RELIPAY_SECRET` directly in a route handler to skip session). The whole point is the session check is the chokepoint.
59
+ - Don't loosen `httpOnly` or `sameSite=lax` without a reason. They're aligned with the panel and the demo for consistency.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared cookie names + helpers. Used by both middleware (Edge runtime) and
3
+ * server-component code (Node runtime) — must stay edge-compatible
4
+ * (no Node-only deps, no `node:crypto`).
5
+ */
6
+ export declare const ACCESS_COOKIE = "relipay_access";
7
+ export declare const REFRESH_COOKIE = "relipay_refresh";
8
+ export interface CookieOptions {
9
+ httpOnly?: boolean;
10
+ sameSite?: 'strict' | 'lax' | 'none';
11
+ secure?: boolean;
12
+ path?: string;
13
+ maxAge?: number;
14
+ }
15
+ export declare const ACCESS_COOKIE_OPTS: CookieOptions;
16
+ export declare const REFRESH_COOKIE_OPTS: CookieOptions;
17
+ //# sourceMappingURL=cookies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,aAAa,mBAAmB,CAAC;AAC9C,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAcD,eAAO,MAAM,kBAAkB,EAAE,aAMhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,aAMjC,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Shared cookie names + helpers. Used by both middleware (Edge runtime) and
3
+ * server-component code (Node runtime) — must stay edge-compatible
4
+ * (no Node-only deps, no `node:crypto`).
5
+ */
6
+ export const ACCESS_COOKIE = 'relipay_access';
7
+ export const REFRESH_COOKIE = 'relipay_refresh';
8
+ /**
9
+ * `secure: true` instructs browsers to refuse setting the cookie over plain
10
+ * HTTP. That is the only correct posture in production. In local dev over
11
+ * http://localhost it silently drops the cookie and the user appears to be
12
+ * permanently signed-out — env-branch keeps both environments working
13
+ * without forcing developers to run a TLS terminator locally.
14
+ *
15
+ * Edge-runtime compatible: `process.env.NODE_ENV` is available in both Edge
16
+ * and Node Next.js runtimes (statically inlined by the bundler).
17
+ */
18
+ const IS_PROD = process.env.NODE_ENV === 'production';
19
+ export const ACCESS_COOKIE_OPTS = {
20
+ httpOnly: true,
21
+ sameSite: 'lax',
22
+ secure: IS_PROD,
23
+ path: '/',
24
+ maxAge: 60 * 15, // 15 minutes (matches access token lifetime)
25
+ };
26
+ export const REFRESH_COOKIE_OPTS = {
27
+ httpOnly: true,
28
+ sameSite: 'lax',
29
+ secure: IS_PROD,
30
+ path: '/',
31
+ maxAge: 60 * 60 * 24 * 30, // 30 days
32
+ };
33
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAUhD;;;;;;;;;GASG;AACH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAEtD,MAAM,CAAC,MAAM,kBAAkB,GAAkB;IAC/C,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,KAAK;IACf,MAAM,EAAE,OAAO;IACf,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,6CAA6C;CAC/D,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAkB;IAChD,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,KAAK;IACf,MAAM,EAAE,OAAO;IACf,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU;CACtC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @relipay/nextjs — Next.js helpers for ReliPay.
3
+ *
4
+ * Two entrypoints:
5
+ *
6
+ * `@relipay/nextjs/middleware` — relipayMiddleware() for middleware.ts
7
+ * `@relipay/nextjs/server` — auth() / signIn() / signOut() server-side
8
+ *
9
+ * Re-exported here for convenience. Direct subpath imports keep edge bundle
10
+ * size minimal when you only need the middleware piece.
11
+ */
12
+ export { relipayMiddleware } from './middleware.js';
13
+ export { auth, signIn, signUp, signOut } from './server.js';
14
+ export type { Session } from './server.js';
15
+ export type { MiddlewareConfig } from './middleware.js';
16
+ export { ACCESS_COOKIE, REFRESH_COOKIE, ACCESS_COOKIE_OPTS, REFRESH_COOKIE_OPTS, } from './cookies.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @relipay/nextjs — Next.js helpers for ReliPay.
3
+ *
4
+ * Two entrypoints:
5
+ *
6
+ * `@relipay/nextjs/middleware` — relipayMiddleware() for middleware.ts
7
+ * `@relipay/nextjs/server` — auth() / signIn() / signOut() server-side
8
+ *
9
+ * Re-exported here for convenience. Direct subpath imports keep edge bundle
10
+ * size minimal when you only need the middleware piece.
11
+ */
12
+ export { relipayMiddleware } from './middleware.js';
13
+ export { auth, signIn, signUp, signOut } from './server.js';
14
+ export { ACCESS_COOKIE, REFRESH_COOKIE, ACCESS_COOKIE_OPTS, REFRESH_COOKIE_OPTS, } from './cookies.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG5D,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Next.js middleware helpers.
3
+ *
4
+ * `relipayMiddleware({ publicRoutes, signInUrl })` returns a middleware
5
+ * function the user wires up in their `middleware.ts`. It:
6
+ * - Lets `publicRoutes` pass through unauthenticated.
7
+ * - For protected routes, requires the access cookie. Missing → redirect
8
+ * to `signInUrl` with a `next` query param so the user lands back here
9
+ * after sign-in.
10
+ *
11
+ * This middleware is intentionally simple — it does not call ReliPay over
12
+ * the network on every request. Token validity is verified the next time
13
+ * the customer's server uses it via `auth()` or directly. The cookie's
14
+ * presence is the gate; the cookie's *value* is checked deeper in the stack.
15
+ */
16
+ import { NextResponse, type NextRequest } from 'next/server';
17
+ export interface MiddlewareConfig {
18
+ /** Routes that don't require auth. Strings or RegExp; matched against pathname. */
19
+ publicRoutes?: Array<string | RegExp>;
20
+ /** Where to send unauthenticated users. Defaults to /sign-in. */
21
+ signInUrl?: string;
22
+ }
23
+ export declare function relipayMiddleware(config?: MiddlewareConfig): (req: NextRequest) => NextResponse;
24
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG7D,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACtC,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,gBAAqB,IAUlC,KAAK,WAAW,KAAG,YAAY,CAY3D"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Next.js middleware helpers.
3
+ *
4
+ * `relipayMiddleware({ publicRoutes, signInUrl })` returns a middleware
5
+ * function the user wires up in their `middleware.ts`. It:
6
+ * - Lets `publicRoutes` pass through unauthenticated.
7
+ * - For protected routes, requires the access cookie. Missing → redirect
8
+ * to `signInUrl` with a `next` query param so the user lands back here
9
+ * after sign-in.
10
+ *
11
+ * This middleware is intentionally simple — it does not call ReliPay over
12
+ * the network on every request. Token validity is verified the next time
13
+ * the customer's server uses it via `auth()` or directly. The cookie's
14
+ * presence is the gate; the cookie's *value* is checked deeper in the stack.
15
+ */
16
+ import { NextResponse } from 'next/server';
17
+ import { ACCESS_COOKIE } from './cookies.js';
18
+ function matches(pathname, patterns) {
19
+ return patterns.some((p) => typeof p === 'string' ? pathname === p || pathname.startsWith(p + '/') : p.test(pathname));
20
+ }
21
+ export function relipayMiddleware(config = {}) {
22
+ const publicRoutes = config.publicRoutes ?? [
23
+ '/sign-in',
24
+ '/sign-up',
25
+ '/forgot-password',
26
+ '/reset-password',
27
+ '/api/auth',
28
+ ];
29
+ const signInUrl = config.signInUrl ?? '/sign-in';
30
+ return function middleware(req) {
31
+ const { pathname } = req.nextUrl;
32
+ if (matches(pathname, publicRoutes))
33
+ return NextResponse.next();
34
+ const access = req.cookies.get(ACCESS_COOKIE)?.value;
35
+ if (access)
36
+ return NextResponse.next();
37
+ const url = req.nextUrl.clone();
38
+ url.pathname = signInUrl;
39
+ url.searchParams.set('next', pathname);
40
+ return NextResponse.redirect(url);
41
+ };
42
+ }
43
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAS7C,SAAS,OAAO,CAAC,QAAgB,EAAE,QAAgC;IACjE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACzB,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAC1F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAA2B,EAAE;IAC7D,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI;QAC1C,UAAU;QACV,UAAU;QACV,kBAAkB;QAClB,iBAAiB;QACjB,WAAW;KACZ,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,UAAU,CAAC;IAEjD,OAAO,SAAS,UAAU,CAAC,GAAgB;QACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;QACjC,IAAI,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC;YAAE,OAAO,YAAY,CAAC,IAAI,EAAE,CAAC;QAEhE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;QACrD,IAAI,MAAM;YAAE,OAAO,YAAY,CAAC,IAAI,EAAE,CAAC;QAEvC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAChC,GAAG,CAAC,QAAQ,GAAG,SAAS,CAAC;QACzB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACvC,OAAO,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Server-side helpers for App Router server components, server actions,
3
+ * and route handlers.
4
+ *
5
+ * Pattern:
6
+ * import { auth, signIn, signOut } from '@relipay/nextjs/server';
7
+ *
8
+ * // In a server component:
9
+ * const session = await auth(); // null when signed out, { user, accessToken } otherwise
10
+ *
11
+ * // In a server action:
12
+ * await signIn({ email, password }); // sets cookies + returns the user
13
+ * await signOut(); // revokes refresh + clears cookies
14
+ *
15
+ * The helpers expect:
16
+ * - `process.env.RELIPAY_URL` (the API URL)
17
+ * - `process.env.RELIPAY_SECRET` (the Application secret key, server-only)
18
+ *
19
+ * Pure server module — never bundled to the browser.
20
+ */
21
+ import type { EndUserDto } from '@relipay/shared-types';
22
+ export interface Session {
23
+ user: EndUserDto;
24
+ /** The access JWT — pass it to client components via the provider's `accessToken`. */
25
+ accessToken: string;
26
+ }
27
+ /**
28
+ * Resolve the current session from cookies. Tries access first; on
29
+ * USER_TOKEN_INVALID, attempts refresh-and-retry once. Returns null when
30
+ * signed out (or when refresh fails).
31
+ *
32
+ * Use from any server component. Cookie writes work in server actions and
33
+ * route handlers (Next.js limitation: not in pure server-component reads).
34
+ */
35
+ export declare function auth(): Promise<Session | null>;
36
+ /**
37
+ * Sign-in outcome for the Next SDK. `kind === "session"` is the happy path;
38
+ * `kind === "mfa_required"` means the user has MFA enrolled and the caller
39
+ * must collect a TOTP / backup code and call `mfaVerify` to complete.
40
+ *
41
+ * No cookies are set on the `mfa_required` branch — the challenge token is
42
+ * NOT a session and must never land in `relipay_access`.
43
+ */
44
+ export type SignInOutcome = {
45
+ kind: 'session';
46
+ session: Session;
47
+ } | {
48
+ kind: 'mfa_required';
49
+ mfaChallengeToken: string;
50
+ mfaChallengeExpiresAt: string;
51
+ user: EndUserDto;
52
+ };
53
+ /**
54
+ * Server action: sign in with email + password.
55
+ *
56
+ * Branches on the server's MFA verdict:
57
+ * - No MFA enrolled → cookies are set and `{ kind: "session" }` is returned.
58
+ * - MFA enrolled → `{ kind: "mfa_required", mfaChallengeToken }` is
59
+ * returned with **no cookies**. Pass the challenge
60
+ * token + the user's code to `mfaVerify(...)` to
61
+ * complete.
62
+ */
63
+ export declare function signIn(input: {
64
+ email: string;
65
+ password: string;
66
+ }): Promise<SignInOutcome>;
67
+ /**
68
+ * Server action: complete an MFA-required sign-in. Sets cookies on success.
69
+ * Throws `RelipayError` with code `MFA_CODE_INVALID` /
70
+ * `MFA_CHALLENGE_INVALID` on failure — surface the error message to the
71
+ * user and prompt to retry.
72
+ */
73
+ export declare function mfaVerify(input: {
74
+ mfaChallengeToken: string;
75
+ code: string;
76
+ }): Promise<Session>;
77
+ /**
78
+ * Server action: sign up + create the user + start a session.
79
+ *
80
+ * Sign-up never returns mfa-required (the new user can't have MFA enrolled
81
+ * yet), so this always sets cookies.
82
+ */
83
+ export declare function signUp(input: {
84
+ email: string;
85
+ password: string;
86
+ metadata?: Record<string, unknown>;
87
+ }): Promise<Session>;
88
+ /**
89
+ * Server action: revoke the refresh token + clear cookies. Optionally
90
+ * pass `redirectTo` to bounce afterwards.
91
+ */
92
+ export declare function signOut(redirectTo?: string): Promise<void>;
93
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAsBxD,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,sFAAsF;IACtF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA4BpD;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACrC;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAQN;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,aAAa,CAAC,CAezB;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,OAAO,CAAC,CAInB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,OAAO,CAAC,OAAO,CAAC,CAInB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAShE"}
package/dist/server.js ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Server-side helpers for App Router server components, server actions,
3
+ * and route handlers.
4
+ *
5
+ * Pattern:
6
+ * import { auth, signIn, signOut } from '@relipay/nextjs/server';
7
+ *
8
+ * // In a server component:
9
+ * const session = await auth(); // null when signed out, { user, accessToken } otherwise
10
+ *
11
+ * // In a server action:
12
+ * await signIn({ email, password }); // sets cookies + returns the user
13
+ * await signOut(); // revokes refresh + clears cookies
14
+ *
15
+ * The helpers expect:
16
+ * - `process.env.RELIPAY_URL` (the API URL)
17
+ * - `process.env.RELIPAY_SECRET` (the Application secret key, server-only)
18
+ *
19
+ * Pure server module — never bundled to the browser.
20
+ */
21
+ import { cookies } from 'next/headers';
22
+ import { redirect } from 'next/navigation';
23
+ import { ReliPay, RelipayError } from '@relipay/node';
24
+ import { ACCESS_COOKIE, REFRESH_COOKIE, ACCESS_COOKIE_OPTS, REFRESH_COOKIE_OPTS, } from './cookies.js';
25
+ let _client = null;
26
+ function client() {
27
+ if (_client)
28
+ return _client;
29
+ const apiUrl = process.env.RELIPAY_URL;
30
+ const secretKey = process.env.RELIPAY_SECRET;
31
+ if (!apiUrl || !secretKey) {
32
+ throw new Error('@relipay/nextjs: RELIPAY_URL and RELIPAY_SECRET must be set on the server.');
33
+ }
34
+ _client = new ReliPay({ apiUrl, secretKey });
35
+ return _client;
36
+ }
37
+ /**
38
+ * Resolve the current session from cookies. Tries access first; on
39
+ * USER_TOKEN_INVALID, attempts refresh-and-retry once. Returns null when
40
+ * signed out (or when refresh fails).
41
+ *
42
+ * Use from any server component. Cookie writes work in server actions and
43
+ * route handlers (Next.js limitation: not in pure server-component reads).
44
+ */
45
+ export async function auth() {
46
+ const jar = await cookies();
47
+ const access = jar.get(ACCESS_COOKIE)?.value;
48
+ if (access) {
49
+ try {
50
+ const user = await client().auth.getCurrentUser(access);
51
+ return { user, accessToken: access };
52
+ }
53
+ catch (err) {
54
+ if (!(err instanceof RelipayError) || err.code !== 'USER_TOKEN_INVALID') {
55
+ throw err;
56
+ }
57
+ }
58
+ }
59
+ // Try refresh.
60
+ const refresh = jar.get(REFRESH_COOKIE)?.value;
61
+ if (!refresh)
62
+ return null;
63
+ try {
64
+ const fresh = await client().auth.refresh(refresh);
65
+ jar.set(ACCESS_COOKIE, fresh.accessToken, ACCESS_COOKIE_OPTS);
66
+ jar.set(REFRESH_COOKIE, fresh.refreshToken, REFRESH_COOKIE_OPTS);
67
+ const user = await client().auth.getCurrentUser(fresh.accessToken);
68
+ return { user, accessToken: fresh.accessToken };
69
+ }
70
+ catch {
71
+ jar.delete(ACCESS_COOKIE);
72
+ jar.delete(REFRESH_COOKIE);
73
+ return null;
74
+ }
75
+ }
76
+ async function setSessionCookies(accessToken, refreshToken) {
77
+ const jar = await cookies();
78
+ jar.set(ACCESS_COOKIE, accessToken, ACCESS_COOKIE_OPTS);
79
+ jar.set(REFRESH_COOKIE, refreshToken, REFRESH_COOKIE_OPTS);
80
+ }
81
+ /**
82
+ * Server action: sign in with email + password.
83
+ *
84
+ * Branches on the server's MFA verdict:
85
+ * - No MFA enrolled → cookies are set and `{ kind: "session" }` is returned.
86
+ * - MFA enrolled → `{ kind: "mfa_required", mfaChallengeToken }` is
87
+ * returned with **no cookies**. Pass the challenge
88
+ * token + the user's code to `mfaVerify(...)` to
89
+ * complete.
90
+ */
91
+ export async function signIn(input) {
92
+ const result = await client().auth.signIn(input);
93
+ if (result.mfaRequired) {
94
+ return {
95
+ kind: 'mfa_required',
96
+ mfaChallengeToken: result.mfaChallengeToken,
97
+ mfaChallengeExpiresAt: result.mfaChallengeExpiresAt,
98
+ user: result.endUser,
99
+ };
100
+ }
101
+ await setSessionCookies(result.accessToken, result.refreshToken);
102
+ return {
103
+ kind: 'session',
104
+ session: { user: result.endUser, accessToken: result.accessToken },
105
+ };
106
+ }
107
+ /**
108
+ * Server action: complete an MFA-required sign-in. Sets cookies on success.
109
+ * Throws `RelipayError` with code `MFA_CODE_INVALID` /
110
+ * `MFA_CHALLENGE_INVALID` on failure — surface the error message to the
111
+ * user and prompt to retry.
112
+ */
113
+ export async function mfaVerify(input) {
114
+ const result = await client().auth.mfaVerify(input);
115
+ await setSessionCookies(result.accessToken, result.refreshToken);
116
+ return { user: result.endUser, accessToken: result.accessToken };
117
+ }
118
+ /**
119
+ * Server action: sign up + create the user + start a session.
120
+ *
121
+ * Sign-up never returns mfa-required (the new user can't have MFA enrolled
122
+ * yet), so this always sets cookies.
123
+ */
124
+ export async function signUp(input) {
125
+ const result = await client().auth.signUp(input);
126
+ await setSessionCookies(result.accessToken, result.refreshToken);
127
+ return { user: result.endUser, accessToken: result.accessToken };
128
+ }
129
+ /**
130
+ * Server action: revoke the refresh token + clear cookies. Optionally
131
+ * pass `redirectTo` to bounce afterwards.
132
+ */
133
+ export async function signOut(redirectTo) {
134
+ const jar = await cookies();
135
+ const refresh = jar.get(REFRESH_COOKIE)?.value;
136
+ if (refresh) {
137
+ await client().auth.signOut(refresh).catch(() => undefined);
138
+ }
139
+ jar.delete(ACCESS_COOKIE);
140
+ jar.delete(REFRESH_COOKIE);
141
+ if (redirectTo)
142
+ redirect(redirectTo);
143
+ }
144
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@relipay/nextjs",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "ReliPay helpers for Next.js — middleware, server-side auth(), cookie session.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./middleware": {
14
+ "types": "./dist/middleware.d.ts",
15
+ "import": "./dist/middleware.js"
16
+ },
17
+ "./server": {
18
+ "types": "./dist/server.d.ts",
19
+ "import": "./dist/server.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "AGENTS.md",
25
+ "README.md"
26
+ ],
27
+ "keywords": [
28
+ "next.js",
29
+ "auth",
30
+ "middleware",
31
+ "relipay"
32
+ ],
33
+ "peerDependencies": {
34
+ "next": "^14.0.0 || ^15.0.0",
35
+ "react": "^18.0.0 || ^19.0.0"
36
+ },
37
+ "dependencies": {
38
+ "@relipay/shared-types": "0.1.0-beta.0",
39
+ "@relipay/node": "0.1.0-beta.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.9.0",
43
+ "next": "^15.1.0",
44
+ "react": "^19.0.0",
45
+ "typescript": "^5.6.3",
46
+ "vitest": "^2.1.5"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "tag": "beta"
51
+ },
52
+ "scripts": {
53
+ "build": "tsc -p tsconfig.json",
54
+ "typecheck": "tsc -p tsconfig.json --noEmit",
55
+ "lint": "echo \"(eslint not yet wired)\"",
56
+ "test": "echo \"(nextjs SDK tests deferred to panel/demo e2e)\""
57
+ }
58
+ }