@rpcbase/auth 0.84.0 → 0.85.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/README.md +87 -0
- package/dist/api/oauth/callback/handler.d.ts +5 -0
- package/dist/api/oauth/callback/handler.d.ts.map +1 -0
- package/dist/api/oauth/callback/index.d.ts +10 -0
- package/dist/api/oauth/callback/index.d.ts.map +1 -0
- package/dist/api/oauth/start/handler.d.ts +5 -0
- package/dist/api/oauth/start/handler.d.ts.map +1 -0
- package/dist/api/oauth/start/index.d.ts +10 -0
- package/dist/api/oauth/start/index.d.ts.map +1 -0
- package/dist/components/AuthLayout/index.d.ts.map +1 -1
- package/dist/components/SignInForm/index.d.ts.map +1 -1
- package/dist/handler-BXyRZsH5.js +81 -0
- package/dist/handler-BXyRZsH5.js.map +1 -0
- package/dist/handler-n7Lp9zg7.js +235 -0
- package/dist/handler-n7Lp9zg7.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/oauth/config.d.ts +10 -0
- package/dist/oauth/config.d.ts.map +1 -0
- package/dist/oauth/jwt.d.ts +2 -0
- package/dist/oauth/jwt.d.ts.map +1 -0
- package/dist/oauth/oidc.d.ts +9 -0
- package/dist/oauth/oidc.d.ts.map +1 -0
- package/dist/oauth/pkce.d.ts +6 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/providerId.d.ts +2 -0
- package/dist/oauth/providerId.d.ts.map +1 -0
- package/dist/providerId-Clk38lTZ.js +59 -0
- package/dist/providerId-Clk38lTZ.js.map +1 -0
- package/dist/routes-ChxNWaaP.js +56 -0
- package/dist/routes-ChxNWaaP.js.map +1 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +4 -7
- package/dist/routes.js.map +1 -1
- package/dist/utils/sanitizeNextPath.d.ts +2 -0
- package/dist/utils/sanitizeNextPath.d.ts.map +1 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# `@rpcbase/auth`
|
|
2
|
+
|
|
3
|
+
UI helpers + API routes for authentication.
|
|
4
|
+
|
|
5
|
+
## OAuth (OIDC Authorization Code + PKCE)
|
|
6
|
+
|
|
7
|
+
OAuth support lives behind two framework routes:
|
|
8
|
+
|
|
9
|
+
- `GET /api/rb/auth/oauth/:provider/start` (initiates the flow)
|
|
10
|
+
- `GET /api/rb/auth/oauth/:provider/callback` (handles the return from the provider)
|
|
11
|
+
|
|
12
|
+
`provider` is the provider id you configure (for example `mock`, `google`, `github`).
|
|
13
|
+
|
|
14
|
+
### 1) Register auth routes in your API
|
|
15
|
+
|
|
16
|
+
Example (like `sample-app`):
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { initApi } from "@rpcbase/api"
|
|
20
|
+
import { routes as authRoutes } from "@rpcbase/auth/routes"
|
|
21
|
+
|
|
22
|
+
const routes = {
|
|
23
|
+
...authRoutes,
|
|
24
|
+
...import.meta.glob("./**/handler.ts"),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const runApi = async ({ app }) => {
|
|
28
|
+
await initApi({ app, routes })
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
These routes require an Express session to be available (used to store `state` + PKCE verifier between `start` and `callback`).
|
|
33
|
+
|
|
34
|
+
### 2) Configure providers
|
|
35
|
+
|
|
36
|
+
Provider configuration is provided by the host app (server-side) at runtime.
|
|
37
|
+
|
|
38
|
+
Call `configureOAuthProviders()` before initializing your API routes:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { initApi } from "@rpcbase/api"
|
|
42
|
+
import { configureOAuthProviders, routes as authRoutes } from "@rpcbase/auth/routes"
|
|
43
|
+
|
|
44
|
+
configureOAuthProviders({
|
|
45
|
+
mock: {
|
|
46
|
+
issuer: "http://localhost:9400",
|
|
47
|
+
clientId: "rpcbase-sample-app",
|
|
48
|
+
scope: "openid email profile",
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const routes = {
|
|
53
|
+
...authRoutes,
|
|
54
|
+
...import.meta.glob("./**/handler.ts"),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const runApi = async ({ app }) => {
|
|
58
|
+
await initApi({ app, routes })
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The app is responsible for sourcing secrets (env, vault, etc.) and passing them to `configureOAuthProviders()`.
|
|
63
|
+
|
|
64
|
+
### 3) Start the OAuth flow from the client
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
window.location.assign("/api/rb/auth/oauth/mock/start")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 4) What happens on callback
|
|
71
|
+
|
|
72
|
+
On a successful callback:
|
|
73
|
+
|
|
74
|
+
- tokens are exchanged (`/token`)
|
|
75
|
+
- userinfo is fetched (when supported by the provider)
|
|
76
|
+
- the matching `RBUser` is created on first-seen identity (and the OAuth credentials are saved under `RBUser.oauthProviders[provider]`)
|
|
77
|
+
- the session is signed in, and the user is redirected to `/onboarding`
|
|
78
|
+
|
|
79
|
+
### Mock provider (dev/tests)
|
|
80
|
+
|
|
81
|
+
This repo uses `oauth2-mock-server` in `sample-app/server.js` to spin up a local OIDC provider for development and Playwright tests.
|
|
82
|
+
|
|
83
|
+
Notes:
|
|
84
|
+
|
|
85
|
+
- `oauth2-mock-server` simulates auth and immediately redirects back on `/authorize` (it does not provide a UI to pick an account).
|
|
86
|
+
- Disable it with `RB_DISABLE_OAUTH_MOCK=1`
|
|
87
|
+
- Override the port with `RB_OAUTH_MOCK_SERVER_PORT` (default `9400`)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/api/oauth/callback/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;yBA+RrC,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const Route = "/api/rb/auth/oauth/:provider/callback";
|
|
3
|
+
export declare const requestSchema: z.ZodObject<{}, z.core.$strip>;
|
|
4
|
+
export type RequestPayload = z.infer<typeof requestSchema>;
|
|
5
|
+
export declare const responseSchema: z.ZodObject<{
|
|
6
|
+
success: z.ZodBoolean;
|
|
7
|
+
error: z.ZodOptional<z.ZodString>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type ResponsePayload = z.infer<typeof responseSchema>;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/oauth/callback/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,KAAK,0CAA0C,CAAA;AAE5D,eAAO,MAAM,aAAa,gCAAe,CAAA;AAEzC,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AAE1D,eAAO,MAAM,cAAc;;;iBAGzB,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/api/oauth/start/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;yBAwFrC,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const Route = "/api/rb/auth/oauth/:provider/start";
|
|
3
|
+
export declare const requestSchema: z.ZodObject<{}, z.core.$strip>;
|
|
4
|
+
export type RequestPayload = z.infer<typeof requestSchema>;
|
|
5
|
+
export declare const responseSchema: z.ZodObject<{
|
|
6
|
+
success: z.ZodBoolean;
|
|
7
|
+
error: z.ZodOptional<z.ZodString>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type ResponsePayload = z.infer<typeof responseSchema>;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/oauth/start/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,KAAK,uCAAuC,CAAA;AAEzD,eAAO,MAAM,aAAa,gCAAe,CAAA;AAEzC,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AAE1D,eAAO,MAAM,cAAc;;;iBAGzB,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AuthLayout/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,UAAU,GAAI,0BAGxB;IACD,SAAS,EAAE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAA;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AuthLayout/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,UAAU,GAAI,0BAGxB;IACD,SAAS,EAAE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAA;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,4CAyBA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignInForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignInForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAYjC,eAAO,MAAM,UAAU,GAAI,yBAGxB;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,4CAwDA,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { g as getOAuthProviderConfig } from "./routes-ChxNWaaP.js";
|
|
3
|
+
import { i as isSafeOAuthProviderId, g as getOidcWellKnown } from "./providerId-Clk38lTZ.js";
|
|
4
|
+
import { o as object, s as string, b as boolean } from "./schemas-BCQJ62HD.js";
|
|
5
|
+
const base64UrlEncode = (input) => input.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
6
|
+
const generatePkcePair = () => {
|
|
7
|
+
const verifier = base64UrlEncode(crypto.randomBytes(32));
|
|
8
|
+
const challenge = base64UrlEncode(crypto.createHash("sha256").update(verifier).digest());
|
|
9
|
+
return { verifier, challenge };
|
|
10
|
+
};
|
|
11
|
+
const Route = "/api/rb/auth/oauth/:provider/start";
|
|
12
|
+
object({});
|
|
13
|
+
object({
|
|
14
|
+
success: boolean(),
|
|
15
|
+
error: string().optional()
|
|
16
|
+
});
|
|
17
|
+
const getRequestOrigin = (ctx) => {
|
|
18
|
+
const protoHeader = ctx.req.headers["x-forwarded-proto"];
|
|
19
|
+
const hostHeader = ctx.req.headers["x-forwarded-host"];
|
|
20
|
+
const protocol = typeof protoHeader === "string" ? protoHeader.split(",")[0].trim() : ctx.req.protocol;
|
|
21
|
+
const host = typeof hostHeader === "string" ? hostHeader.split(",")[0].trim() : ctx.req.get("host");
|
|
22
|
+
return protocol && host ? `${protocol}://${host}` : "";
|
|
23
|
+
};
|
|
24
|
+
const start = async (_payload, ctx) => {
|
|
25
|
+
const providerId = String(ctx.req.params?.provider ?? "").trim();
|
|
26
|
+
if (!providerId) {
|
|
27
|
+
ctx.res.status(400);
|
|
28
|
+
return { success: false, error: "missing_provider" };
|
|
29
|
+
}
|
|
30
|
+
if (!isSafeOAuthProviderId(providerId)) {
|
|
31
|
+
ctx.res.status(400);
|
|
32
|
+
return { success: false, error: "invalid_provider" };
|
|
33
|
+
}
|
|
34
|
+
const provider = getOAuthProviderConfig(providerId);
|
|
35
|
+
if (!provider) {
|
|
36
|
+
ctx.res.status(404);
|
|
37
|
+
return { success: false, error: "unknown_provider" };
|
|
38
|
+
}
|
|
39
|
+
if (!ctx.req.session) {
|
|
40
|
+
ctx.res.status(500);
|
|
41
|
+
return { success: false, error: "session_unavailable" };
|
|
42
|
+
}
|
|
43
|
+
const origin = getRequestOrigin(ctx);
|
|
44
|
+
if (!origin) {
|
|
45
|
+
ctx.res.status(500);
|
|
46
|
+
return { success: false, error: "origin_unavailable" };
|
|
47
|
+
}
|
|
48
|
+
const state = crypto.randomBytes(16).toString("hex");
|
|
49
|
+
const { verifier, challenge } = generatePkcePair();
|
|
50
|
+
const sessionAny = ctx.req.session;
|
|
51
|
+
const rbOauthRaw = sessionAny.rbOauth;
|
|
52
|
+
if (!rbOauthRaw || typeof rbOauthRaw !== "object" || Array.isArray(rbOauthRaw)) {
|
|
53
|
+
sessionAny.rbOauth = /* @__PURE__ */ Object.create(null);
|
|
54
|
+
} else if (Object.getPrototypeOf(rbOauthRaw) !== null) {
|
|
55
|
+
sessionAny.rbOauth = Object.assign(/* @__PURE__ */ Object.create(null), rbOauthRaw);
|
|
56
|
+
}
|
|
57
|
+
sessionAny.rbOauth[providerId] = {
|
|
58
|
+
state,
|
|
59
|
+
codeVerifier: verifier,
|
|
60
|
+
createdAt: Date.now()
|
|
61
|
+
};
|
|
62
|
+
const oidc = await getOidcWellKnown(provider.issuer);
|
|
63
|
+
const redirectUri = `${origin}/api/rb/auth/oauth/${encodeURIComponent(providerId)}/callback`;
|
|
64
|
+
const url = new URL(oidc.authorization_endpoint);
|
|
65
|
+
url.searchParams.set("client_id", provider.clientId);
|
|
66
|
+
url.searchParams.set("redirect_uri", redirectUri);
|
|
67
|
+
url.searchParams.set("response_type", "code");
|
|
68
|
+
url.searchParams.set("scope", provider.scope ?? "openid email profile");
|
|
69
|
+
url.searchParams.set("state", state);
|
|
70
|
+
url.searchParams.set("code_challenge", challenge);
|
|
71
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
72
|
+
ctx.res.redirect(url.toString());
|
|
73
|
+
return { success: true };
|
|
74
|
+
};
|
|
75
|
+
const handler = (api) => {
|
|
76
|
+
api.get(Route, start);
|
|
77
|
+
};
|
|
78
|
+
export {
|
|
79
|
+
handler as default
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=handler-BXyRZsH5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-BXyRZsH5.js","sources":["../src/oauth/pkce.ts","../src/api/oauth/start/index.ts","../src/api/oauth/start/handler.ts"],"sourcesContent":["import crypto from \"crypto\"\n\n\nexport const base64UrlEncode = (input: Buffer) => input\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\")\n\nexport const generatePkcePair = () => {\n const verifier = base64UrlEncode(crypto.randomBytes(32))\n const challenge = base64UrlEncode(crypto.createHash(\"sha256\").update(verifier).digest())\n\n return { verifier, challenge }\n}\n\n","import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/oauth/:provider/start\"\n\nexport const requestSchema = z.object({})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\n\nimport type { AuthSessionUser } from \"../../../types\"\nimport { getOAuthProviderConfig } from \"../../../oauth/config\"\nimport { getOidcWellKnown } from \"../../../oauth/oidc\"\nimport { isSafeOAuthProviderId } from \"../../../oauth/providerId\"\nimport { generatePkcePair } from \"../../../oauth/pkce\"\n\nimport * as OAuthStart from \"./index\"\n\n\nconst getRequestOrigin = (ctx: Ctx<AuthSessionUser>) => {\n const protoHeader = ctx.req.headers[\"x-forwarded-proto\"]\n const hostHeader = ctx.req.headers[\"x-forwarded-host\"]\n\n const protocol = typeof protoHeader === \"string\"\n ? protoHeader.split(\",\")[0].trim()\n : ctx.req.protocol\n\n const host = typeof hostHeader === \"string\"\n ? hostHeader.split(\",\")[0].trim()\n : ctx.req.get(\"host\")\n\n return protocol && host ? `${protocol}://${host}` : \"\"\n}\n\nconst start: ApiHandler<OAuthStart.RequestPayload, OAuthStart.ResponsePayload, AuthSessionUser> = async (\n _payload,\n ctx,\n) => {\n const providerId = String(ctx.req.params?.provider ?? \"\").trim()\n if (!providerId) {\n ctx.res.status(400)\n return { success: false, error: \"missing_provider\" }\n }\n\n if (!isSafeOAuthProviderId(providerId)) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_provider\" }\n }\n\n const provider = getOAuthProviderConfig(providerId)\n if (!provider) {\n ctx.res.status(404)\n return { success: false, error: \"unknown_provider\" }\n }\n\n if (!ctx.req.session) {\n ctx.res.status(500)\n return { success: false, error: \"session_unavailable\" }\n }\n\n const origin = getRequestOrigin(ctx)\n if (!origin) {\n ctx.res.status(500)\n return { success: false, error: \"origin_unavailable\" }\n }\n\n const state = crypto.randomBytes(16).toString(\"hex\")\n const { verifier, challenge } = generatePkcePair()\n\n const sessionAny = ctx.req.session as any\n const rbOauthRaw = sessionAny.rbOauth\n if (!rbOauthRaw || typeof rbOauthRaw !== \"object\" || Array.isArray(rbOauthRaw)) {\n sessionAny.rbOauth = Object.create(null)\n } else if (Object.getPrototypeOf(rbOauthRaw) !== null) {\n sessionAny.rbOauth = Object.assign(Object.create(null), rbOauthRaw)\n }\n sessionAny.rbOauth[providerId] = {\n state,\n codeVerifier: verifier,\n createdAt: Date.now(),\n }\n\n const oidc = await getOidcWellKnown(provider.issuer)\n const redirectUri = `${origin}/api/rb/auth/oauth/${encodeURIComponent(providerId)}/callback`\n\n const url = new URL(oidc.authorization_endpoint)\n url.searchParams.set(\"client_id\", provider.clientId)\n url.searchParams.set(\"redirect_uri\", redirectUri)\n url.searchParams.set(\"response_type\", \"code\")\n url.searchParams.set(\"scope\", provider.scope ?? \"openid email profile\")\n url.searchParams.set(\"state\", state)\n url.searchParams.set(\"code_challenge\", challenge)\n url.searchParams.set(\"code_challenge_method\", \"S256\")\n\n ctx.res.redirect(url.toString())\n return { success: true }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.get(OAuthStart.Route, start)\n}\n"],"names":["z.object","z.boolean","z.string","OAuthStart.Route"],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { models } from "@rpcbase/db";
|
|
3
|
+
import { hashPasswordForStorage } from "@rpcbase/server";
|
|
4
|
+
import { g as getOAuthProviderConfig } from "./routes-ChxNWaaP.js";
|
|
5
|
+
import { i as isSafeOAuthProviderId, g as getOidcWellKnown } from "./providerId-Clk38lTZ.js";
|
|
6
|
+
import { o as object, s as string, b as boolean } from "./schemas-BCQJ62HD.js";
|
|
7
|
+
const decodeBase64Url = (value) => {
|
|
8
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((value.length + 3) % 4);
|
|
9
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
10
|
+
};
|
|
11
|
+
const decodeJwtPayload = (token) => {
|
|
12
|
+
const parts = token.split(".");
|
|
13
|
+
if (parts.length < 2) return null;
|
|
14
|
+
try {
|
|
15
|
+
const json = decodeBase64Url(parts[1]);
|
|
16
|
+
const parsed = JSON.parse(json);
|
|
17
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
18
|
+
return parsed;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const Route = "/api/rb/auth/oauth/:provider/callback";
|
|
24
|
+
object({});
|
|
25
|
+
object({
|
|
26
|
+
success: boolean(),
|
|
27
|
+
error: string().optional()
|
|
28
|
+
});
|
|
29
|
+
const getQueryString = (value) => {
|
|
30
|
+
if (typeof value === "string") return value;
|
|
31
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const getRequestOrigin = (ctx) => {
|
|
35
|
+
const protoHeader = ctx.req.headers["x-forwarded-proto"];
|
|
36
|
+
const hostHeader = ctx.req.headers["x-forwarded-host"];
|
|
37
|
+
const protocol = typeof protoHeader === "string" ? protoHeader.split(",")[0].trim() : ctx.req.protocol;
|
|
38
|
+
const host = typeof hostHeader === "string" ? hostHeader.split(",")[0].trim() : ctx.req.get("host");
|
|
39
|
+
return protocol && host ? `${protocol}://${host}` : "";
|
|
40
|
+
};
|
|
41
|
+
const OAUTH_SUCCESS_REDIRECT_PATH = "/onboarding";
|
|
42
|
+
const callback = async (_payload, ctx) => {
|
|
43
|
+
const providerId = String(ctx.req.params?.provider ?? "").trim();
|
|
44
|
+
if (!providerId) {
|
|
45
|
+
ctx.res.redirect("/auth/sign-in?error=missing_provider");
|
|
46
|
+
return { success: false, error: "missing_provider" };
|
|
47
|
+
}
|
|
48
|
+
if (!isSafeOAuthProviderId(providerId)) {
|
|
49
|
+
ctx.res.redirect("/auth/sign-in?error=invalid_provider");
|
|
50
|
+
return { success: false, error: "invalid_provider" };
|
|
51
|
+
}
|
|
52
|
+
const provider = getOAuthProviderConfig(providerId);
|
|
53
|
+
if (!provider) {
|
|
54
|
+
ctx.res.redirect("/auth/sign-in?error=unknown_provider");
|
|
55
|
+
return { success: false, error: "unknown_provider" };
|
|
56
|
+
}
|
|
57
|
+
if (!ctx.req.session) {
|
|
58
|
+
ctx.res.status(500).redirect("/auth/sign-in?error=session_unavailable");
|
|
59
|
+
return { success: false, error: "session_unavailable" };
|
|
60
|
+
}
|
|
61
|
+
const origin = getRequestOrigin(ctx);
|
|
62
|
+
if (!origin) {
|
|
63
|
+
ctx.res.status(500).redirect("/auth/sign-in?error=origin_unavailable");
|
|
64
|
+
return { success: false, error: "origin_unavailable" };
|
|
65
|
+
}
|
|
66
|
+
const code = getQueryString(ctx.req.query?.code)?.trim() ?? "";
|
|
67
|
+
const state = getQueryString(ctx.req.query?.state)?.trim() ?? "";
|
|
68
|
+
const sessionAny = ctx.req.session;
|
|
69
|
+
const sessionState = sessionAny.rbOauth?.[providerId];
|
|
70
|
+
if (!code || !state) {
|
|
71
|
+
ctx.res.redirect("/auth/sign-in?error=missing_code_or_state");
|
|
72
|
+
return { success: false, error: "missing_code_or_state" };
|
|
73
|
+
}
|
|
74
|
+
if (!sessionState || typeof sessionState.state !== "string" || sessionState.state !== state) {
|
|
75
|
+
ctx.res.redirect("/auth/sign-in?error=invalid_state");
|
|
76
|
+
return { success: false, error: "invalid_state" };
|
|
77
|
+
}
|
|
78
|
+
const codeVerifier = typeof sessionState.codeVerifier === "string" ? sessionState.codeVerifier : "";
|
|
79
|
+
if (!codeVerifier) {
|
|
80
|
+
ctx.res.redirect("/auth/sign-in?error=missing_code_verifier");
|
|
81
|
+
return { success: false, error: "missing_code_verifier" };
|
|
82
|
+
}
|
|
83
|
+
delete sessionAny.rbOauth?.[providerId];
|
|
84
|
+
if (sessionAny.rbOauth && Object.keys(sessionAny.rbOauth).length === 0) {
|
|
85
|
+
delete sessionAny.rbOauth;
|
|
86
|
+
}
|
|
87
|
+
const oidc = await getOidcWellKnown(provider.issuer);
|
|
88
|
+
const redirectUri = `${origin}/api/rb/auth/oauth/${encodeURIComponent(providerId)}/callback`;
|
|
89
|
+
const tokenBody = new URLSearchParams();
|
|
90
|
+
tokenBody.set("grant_type", "authorization_code");
|
|
91
|
+
tokenBody.set("code", code);
|
|
92
|
+
tokenBody.set("redirect_uri", redirectUri);
|
|
93
|
+
tokenBody.set("client_id", provider.clientId);
|
|
94
|
+
tokenBody.set("code_verifier", codeVerifier);
|
|
95
|
+
if (provider.clientSecret) {
|
|
96
|
+
tokenBody.set("client_secret", provider.clientSecret);
|
|
97
|
+
}
|
|
98
|
+
const tokenResponse = await fetch(oidc.token_endpoint, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
102
|
+
Accept: "application/json"
|
|
103
|
+
},
|
|
104
|
+
body: tokenBody.toString()
|
|
105
|
+
});
|
|
106
|
+
const tokenJson = await tokenResponse.json().catch(() => null);
|
|
107
|
+
if (!tokenResponse.ok || !tokenJson || typeof tokenJson !== "object") {
|
|
108
|
+
const body = tokenJson ? JSON.stringify(tokenJson) : "";
|
|
109
|
+
console.warn("oauth::token_exchange failed", tokenResponse.status, body);
|
|
110
|
+
ctx.res.redirect("/auth/sign-in?error=token_exchange_failed");
|
|
111
|
+
return { success: false, error: "token_exchange_failed" };
|
|
112
|
+
}
|
|
113
|
+
const accessToken = typeof tokenJson.access_token === "string" ? tokenJson.access_token : "";
|
|
114
|
+
const refreshToken = typeof tokenJson.refresh_token === "string" ? tokenJson.refresh_token : void 0;
|
|
115
|
+
const idToken = typeof tokenJson.id_token === "string" ? tokenJson.id_token : void 0;
|
|
116
|
+
const scope = typeof tokenJson.scope === "string" ? tokenJson.scope : void 0;
|
|
117
|
+
const tokenType = typeof tokenJson.token_type === "string" ? tokenJson.token_type : void 0;
|
|
118
|
+
const expiresIn = typeof tokenJson.expires_in === "number" ? tokenJson.expires_in : typeof tokenJson.expires_in === "string" ? Number(tokenJson.expires_in) : void 0;
|
|
119
|
+
const expiresInSeconds = typeof expiresIn === "number" && Number.isFinite(expiresIn) ? expiresIn : null;
|
|
120
|
+
const expiresAt = expiresInSeconds !== null ? new Date(Date.now() + expiresInSeconds * 1e3) : void 0;
|
|
121
|
+
if (!accessToken) {
|
|
122
|
+
ctx.res.redirect("/auth/sign-in?error=missing_access_token");
|
|
123
|
+
return { success: false, error: "missing_access_token" };
|
|
124
|
+
}
|
|
125
|
+
let userInfo = null;
|
|
126
|
+
if (oidc.userinfo_endpoint) {
|
|
127
|
+
const userInfoResponse = await fetch(oidc.userinfo_endpoint, {
|
|
128
|
+
headers: {
|
|
129
|
+
Authorization: `Bearer ${accessToken}`,
|
|
130
|
+
Accept: "application/json"
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
if (userInfoResponse.ok) {
|
|
134
|
+
userInfo = await userInfoResponse.json().catch(() => null);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const idTokenPayload = idToken ? decodeJwtPayload(idToken) : null;
|
|
138
|
+
const accessTokenPayload = decodeJwtPayload(accessToken);
|
|
139
|
+
const subject = typeof userInfo?.sub === "string" ? userInfo.sub : typeof idTokenPayload?.sub === "string" ? idTokenPayload.sub : typeof accessTokenPayload?.sub === "string" ? accessTokenPayload.sub : "";
|
|
140
|
+
if (!subject) {
|
|
141
|
+
ctx.res.redirect("/auth/sign-in?error=missing_subject");
|
|
142
|
+
return { success: false, error: "missing_subject" };
|
|
143
|
+
}
|
|
144
|
+
const email = typeof userInfo?.email === "string" ? userInfo.email : typeof idTokenPayload?.email === "string" ? idTokenPayload.email : void 0;
|
|
145
|
+
const name = typeof userInfo?.name === "string" ? userInfo.name : typeof idTokenPayload?.name === "string" ? idTokenPayload.name : void 0;
|
|
146
|
+
const [User, Tenant] = await Promise.all([
|
|
147
|
+
models.getGlobal("RBUser", ctx),
|
|
148
|
+
models.getGlobal("RBTenant", ctx)
|
|
149
|
+
]);
|
|
150
|
+
const subjectQueryKey = `oauthProviders.${providerId}.subject`;
|
|
151
|
+
let user = await User.findOne({ [subjectQueryKey]: subject });
|
|
152
|
+
if (!user && email) {
|
|
153
|
+
user = await User.findOne({ email });
|
|
154
|
+
}
|
|
155
|
+
const now = /* @__PURE__ */ new Date();
|
|
156
|
+
let providerCreatedAt;
|
|
157
|
+
const oauthProvidersValue = user ? user.oauthProviders : void 0;
|
|
158
|
+
if (oauthProvidersValue instanceof Map) {
|
|
159
|
+
const existing = oauthProvidersValue.get(providerId);
|
|
160
|
+
if (existing?.createdAt instanceof Date) providerCreatedAt = existing.createdAt;
|
|
161
|
+
} else if (oauthProvidersValue && typeof oauthProvidersValue === "object") {
|
|
162
|
+
const existing = oauthProvidersValue[providerId];
|
|
163
|
+
if (existing?.createdAt instanceof Date) providerCreatedAt = existing.createdAt;
|
|
164
|
+
}
|
|
165
|
+
const oauthProviderPayload = {
|
|
166
|
+
subject,
|
|
167
|
+
email,
|
|
168
|
+
name,
|
|
169
|
+
accessToken,
|
|
170
|
+
refreshToken,
|
|
171
|
+
idToken,
|
|
172
|
+
scope,
|
|
173
|
+
tokenType,
|
|
174
|
+
expiresAt,
|
|
175
|
+
rawUserInfo: userInfo ?? void 0,
|
|
176
|
+
createdAt: providerCreatedAt ?? now,
|
|
177
|
+
updatedAt: now
|
|
178
|
+
};
|
|
179
|
+
if (!user) {
|
|
180
|
+
const tenantId2 = crypto.randomUUID();
|
|
181
|
+
const password = await hashPasswordForStorage(crypto.randomBytes(32).toString("hex"));
|
|
182
|
+
user = new User({
|
|
183
|
+
email,
|
|
184
|
+
name,
|
|
185
|
+
password,
|
|
186
|
+
tenants: [tenantId2],
|
|
187
|
+
tenantRoles: {
|
|
188
|
+
[tenantId2]: ["owner"]
|
|
189
|
+
},
|
|
190
|
+
oauthProviders: {
|
|
191
|
+
[providerId]: oauthProviderPayload
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
await user.save();
|
|
195
|
+
try {
|
|
196
|
+
await Tenant.create({
|
|
197
|
+
tenantId: tenantId2,
|
|
198
|
+
name: email || subject
|
|
199
|
+
});
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.warn("oauth::failed_to_create_tenant", err);
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
const setFields = {
|
|
205
|
+
[`oauthProviders.${providerId}`]: oauthProviderPayload
|
|
206
|
+
};
|
|
207
|
+
if (!user.email && email) {
|
|
208
|
+
setFields.email = email;
|
|
209
|
+
}
|
|
210
|
+
if (!user.name && name) {
|
|
211
|
+
setFields.name = name;
|
|
212
|
+
}
|
|
213
|
+
await User.updateOne({ _id: user._id }, { $set: setFields });
|
|
214
|
+
}
|
|
215
|
+
const tenantId = user.tenants?.[0]?.toString?.() || "00000000";
|
|
216
|
+
const signedInTenants = (user.tenants || []).map((t) => t.toString?.() || String(t)) || [tenantId];
|
|
217
|
+
const tenantRolesRaw = user.tenantRoles;
|
|
218
|
+
const tenantRoles = tenantRolesRaw instanceof Map ? Object.fromEntries(tenantRolesRaw.entries()) : tenantRolesRaw && typeof tenantRolesRaw === "object" ? tenantRolesRaw : void 0;
|
|
219
|
+
ctx.req.session.user = {
|
|
220
|
+
id: user._id.toString(),
|
|
221
|
+
currentTenantId: tenantId,
|
|
222
|
+
signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],
|
|
223
|
+
isEntryGateAuthorized: true,
|
|
224
|
+
tenantRoles
|
|
225
|
+
};
|
|
226
|
+
ctx.res.redirect(OAUTH_SUCCESS_REDIRECT_PATH);
|
|
227
|
+
return { success: true };
|
|
228
|
+
};
|
|
229
|
+
const handler = (api) => {
|
|
230
|
+
api.get(Route, callback);
|
|
231
|
+
};
|
|
232
|
+
export {
|
|
233
|
+
handler as default
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=handler-n7Lp9zg7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-n7Lp9zg7.js","sources":["../src/oauth/jwt.ts","../src/api/oauth/callback/index.ts","../src/api/oauth/callback/handler.ts"],"sourcesContent":["const decodeBase64Url = (value: string) => {\n const padded = value.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((value.length + 3) % 4)\n return Buffer.from(padded, \"base64\").toString(\"utf8\")\n}\n\nexport const decodeJwtPayload = <T = Record<string, unknown>>(token: string): T | null => {\n const parts = token.split(\".\")\n if (parts.length < 2) return null\n\n try {\n const json = decodeBase64Url(parts[1])\n const parsed = JSON.parse(json) as T\n if (!parsed || typeof parsed !== \"object\") return null\n return parsed\n } catch {\n return null\n }\n}\n\n","import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/oauth/:provider/callback\"\n\nexport const requestSchema = z.object({})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { hashPasswordForStorage } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../../types\"\nimport { decodeJwtPayload } from \"../../../oauth/jwt\"\nimport { getOAuthProviderConfig } from \"../../../oauth/config\"\nimport { getOidcWellKnown } from \"../../../oauth/oidc\"\nimport { isSafeOAuthProviderId } from \"../../../oauth/providerId\"\n\nimport * as OAuthCallback from \"./index\"\n\n\nconst getQueryString = (value: unknown) => {\n if (typeof value === \"string\") return value\n if (Array.isArray(value) && typeof value[0] === \"string\") return value[0]\n return null\n}\n\nconst getRequestOrigin = (ctx: Ctx<AuthSessionUser>) => {\n const protoHeader = ctx.req.headers[\"x-forwarded-proto\"]\n const hostHeader = ctx.req.headers[\"x-forwarded-host\"]\n\n const protocol = typeof protoHeader === \"string\"\n ? protoHeader.split(\",\")[0].trim()\n : ctx.req.protocol\n\n const host = typeof hostHeader === \"string\"\n ? hostHeader.split(\",\")[0].trim()\n : ctx.req.get(\"host\")\n\n return protocol && host ? `${protocol}://${host}` : \"\"\n}\n\nconst OAUTH_SUCCESS_REDIRECT_PATH = \"/onboarding\"\n\nconst callback: ApiHandler<OAuthCallback.RequestPayload, OAuthCallback.ResponsePayload, AuthSessionUser> = async (\n _payload,\n ctx,\n) => {\n const providerId = String(ctx.req.params?.provider ?? \"\").trim()\n if (!providerId) {\n ctx.res.redirect(\"/auth/sign-in?error=missing_provider\")\n return { success: false, error: \"missing_provider\" }\n }\n\n if (!isSafeOAuthProviderId(providerId)) {\n ctx.res.redirect(\"/auth/sign-in?error=invalid_provider\")\n return { success: false, error: \"invalid_provider\" }\n }\n\n const provider = getOAuthProviderConfig(providerId)\n if (!provider) {\n ctx.res.redirect(\"/auth/sign-in?error=unknown_provider\")\n return { success: false, error: \"unknown_provider\" }\n }\n\n if (!ctx.req.session) {\n ctx.res.status(500).redirect(\"/auth/sign-in?error=session_unavailable\")\n return { success: false, error: \"session_unavailable\" }\n }\n\n const origin = getRequestOrigin(ctx)\n if (!origin) {\n ctx.res.status(500).redirect(\"/auth/sign-in?error=origin_unavailable\")\n return { success: false, error: \"origin_unavailable\" }\n }\n\n const code = getQueryString(ctx.req.query?.code)?.trim() ?? \"\"\n const state = getQueryString(ctx.req.query?.state)?.trim() ?? \"\"\n\n const sessionAny = ctx.req.session as any\n const sessionState = sessionAny.rbOauth?.[providerId]\n\n if (!code || !state) {\n ctx.res.redirect(\"/auth/sign-in?error=missing_code_or_state\")\n return { success: false, error: \"missing_code_or_state\" }\n }\n\n if (!sessionState || typeof sessionState.state !== \"string\" || sessionState.state !== state) {\n ctx.res.redirect(\"/auth/sign-in?error=invalid_state\")\n return { success: false, error: \"invalid_state\" }\n }\n\n const codeVerifier = typeof sessionState.codeVerifier === \"string\" ? sessionState.codeVerifier : \"\"\n if (!codeVerifier) {\n ctx.res.redirect(\"/auth/sign-in?error=missing_code_verifier\")\n return { success: false, error: \"missing_code_verifier\" }\n }\n\n delete sessionAny.rbOauth?.[providerId]\n if (sessionAny.rbOauth && Object.keys(sessionAny.rbOauth).length === 0) {\n delete sessionAny.rbOauth\n }\n\n const oidc = await getOidcWellKnown(provider.issuer)\n const redirectUri = `${origin}/api/rb/auth/oauth/${encodeURIComponent(providerId)}/callback`\n\n const tokenBody = new URLSearchParams()\n tokenBody.set(\"grant_type\", \"authorization_code\")\n tokenBody.set(\"code\", code)\n tokenBody.set(\"redirect_uri\", redirectUri)\n tokenBody.set(\"client_id\", provider.clientId)\n tokenBody.set(\"code_verifier\", codeVerifier)\n if (provider.clientSecret) {\n tokenBody.set(\"client_secret\", provider.clientSecret)\n }\n\n const tokenResponse = await fetch(oidc.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: tokenBody.toString(),\n })\n\n const tokenJson = await tokenResponse.json().catch(() => null) as Record<string, unknown> | null\n\n if (!tokenResponse.ok || !tokenJson || typeof tokenJson !== \"object\") {\n const body = tokenJson ? JSON.stringify(tokenJson) : \"\"\n console.warn(\"oauth::token_exchange failed\", tokenResponse.status, body)\n ctx.res.redirect(\"/auth/sign-in?error=token_exchange_failed\")\n return { success: false, error: \"token_exchange_failed\" }\n }\n\n const accessToken = typeof tokenJson.access_token === \"string\" ? tokenJson.access_token : \"\"\n const refreshToken = typeof tokenJson.refresh_token === \"string\" ? tokenJson.refresh_token : undefined\n const idToken = typeof tokenJson.id_token === \"string\" ? tokenJson.id_token : undefined\n const scope = typeof tokenJson.scope === \"string\" ? tokenJson.scope : undefined\n const tokenType = typeof tokenJson.token_type === \"string\" ? tokenJson.token_type : undefined\n const expiresIn = typeof tokenJson.expires_in === \"number\"\n ? tokenJson.expires_in\n : typeof tokenJson.expires_in === \"string\"\n ? Number(tokenJson.expires_in)\n : undefined\n const expiresInSeconds = typeof expiresIn === \"number\" && Number.isFinite(expiresIn) ? expiresIn : null\n const expiresAt = expiresInSeconds !== null ? new Date(Date.now() + expiresInSeconds * 1000) : undefined\n\n if (!accessToken) {\n ctx.res.redirect(\"/auth/sign-in?error=missing_access_token\")\n return { success: false, error: \"missing_access_token\" }\n }\n\n let userInfo: Record<string, unknown> | null = null\n\n if (oidc.userinfo_endpoint) {\n const userInfoResponse = await fetch(oidc.userinfo_endpoint, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: \"application/json\",\n },\n })\n\n if (userInfoResponse.ok) {\n userInfo = await userInfoResponse.json().catch(() => null) as Record<string, unknown> | null\n }\n }\n\n const idTokenPayload = idToken ? decodeJwtPayload<Record<string, unknown>>(idToken) : null\n const accessTokenPayload = decodeJwtPayload<Record<string, unknown>>(accessToken)\n\n const subject = typeof userInfo?.sub === \"string\"\n ? userInfo.sub\n : typeof idTokenPayload?.sub === \"string\"\n ? idTokenPayload.sub\n : typeof accessTokenPayload?.sub === \"string\"\n ? accessTokenPayload.sub\n : \"\"\n\n if (!subject) {\n ctx.res.redirect(\"/auth/sign-in?error=missing_subject\")\n return { success: false, error: \"missing_subject\" }\n }\n\n const email = typeof userInfo?.email === \"string\"\n ? userInfo.email\n : typeof idTokenPayload?.email === \"string\"\n ? idTokenPayload.email\n : undefined\n\n const name = typeof userInfo?.name === \"string\"\n ? userInfo.name\n : typeof idTokenPayload?.name === \"string\"\n ? idTokenPayload.name\n : undefined\n\n const [User, Tenant] = await Promise.all([\n models.getGlobal(\"RBUser\", ctx),\n models.getGlobal(\"RBTenant\", ctx),\n ])\n\n const subjectQueryKey = `oauthProviders.${providerId}.subject`\n let user = await (User as any).findOne({ [subjectQueryKey]: subject })\n\n if (!user && email) {\n user = await (User as any).findOne({ email })\n }\n\n const now = new Date()\n\n let providerCreatedAt: Date | undefined\n const oauthProvidersValue = user ? (user as any).oauthProviders : undefined\n if (oauthProvidersValue instanceof Map) {\n const existing = oauthProvidersValue.get(providerId)\n if (existing?.createdAt instanceof Date) providerCreatedAt = existing.createdAt\n } else if (oauthProvidersValue && typeof oauthProvidersValue === \"object\") {\n const existing = (oauthProvidersValue as any)[providerId]\n if (existing?.createdAt instanceof Date) providerCreatedAt = existing.createdAt\n }\n\n const oauthProviderPayload = {\n subject,\n email,\n name,\n accessToken,\n refreshToken,\n idToken,\n scope,\n tokenType,\n expiresAt,\n rawUserInfo: userInfo ?? undefined,\n createdAt: providerCreatedAt ?? now,\n updatedAt: now,\n }\n\n if (!user) {\n const tenantId = crypto.randomUUID()\n const password = await hashPasswordForStorage(crypto.randomBytes(32).toString(\"hex\"))\n\n user = new (User as any)({\n email,\n name,\n password,\n tenants: [tenantId],\n tenantRoles: {\n [tenantId]: [\"owner\"],\n },\n oauthProviders: {\n [providerId]: oauthProviderPayload,\n },\n })\n\n await user.save()\n\n try {\n await (Tenant as any).create({\n tenantId,\n name: email || subject,\n })\n } catch (err) {\n console.warn(\"oauth::failed_to_create_tenant\", err)\n }\n } else {\n const setFields: Record<string, unknown> = {\n [`oauthProviders.${providerId}`]: oauthProviderPayload,\n }\n\n if (!user.email && email) {\n setFields.email = email\n }\n\n if (!user.name && name) {\n setFields.name = name\n }\n\n await (User as any).updateOne({ _id: user._id }, { $set: setFields })\n }\n\n const tenantId = user.tenants?.[0]?.toString?.() || \"00000000\"\n const signedInTenants = (user.tenants || []).map((t: any) => t.toString?.() || String(t)) || [tenantId]\n\n const tenantRolesRaw = (user as unknown as { tenantRoles?: unknown }).tenantRoles\n const tenantRoles = tenantRolesRaw instanceof Map\n ? Object.fromEntries(tenantRolesRaw.entries())\n : tenantRolesRaw && typeof tenantRolesRaw === \"object\"\n ? tenantRolesRaw\n : undefined\n\n ctx.req.session.user = {\n id: user._id.toString(),\n currentTenantId: tenantId,\n signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],\n isEntryGateAuthorized: true,\n tenantRoles,\n }\n\n ctx.res.redirect(OAUTH_SUCCESS_REDIRECT_PATH)\n return { success: true }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.get(OAuthCallback.Route, callback)\n}\n"],"names":["z.object","z.boolean","z.string","tenantId","OAuthCallback.Route"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -32,14 +32,14 @@ const AuthLayout = ({
|
|
|
32
32
|
const linkTitle = LINKS_REDIRECTION_MAP[location.pathname]?.title;
|
|
33
33
|
const linkLocation = LINKS_REDIRECTION_MAP[location.pathname]?.location;
|
|
34
34
|
return /* @__PURE__ */ jsxs("div", { className: "container relative hidden h-dvh flex-col items-center justify-center md:grid md:w-full lg:max-w-none lg:grid-cols-2 lg:px-0", children: [
|
|
35
|
-
/* @__PURE__ */ jsx(
|
|
35
|
+
linkTitle && linkLocation ? /* @__PURE__ */ jsx(
|
|
36
36
|
Link,
|
|
37
37
|
{
|
|
38
38
|
className: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2 absolute right-4 top-4 md:right-8 md:top-8",
|
|
39
39
|
to: linkLocation,
|
|
40
40
|
children: linkTitle
|
|
41
41
|
}
|
|
42
|
-
),
|
|
42
|
+
) : null,
|
|
43
43
|
/* @__PURE__ */ jsx("div", { className: "relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex", children: sidePanel }),
|
|
44
44
|
/* @__PURE__ */ jsx("div", { className: "mx-auto flex w-full flex-col justify-center gap-6 /*sm:w-[350px]*/", children })
|
|
45
45
|
] });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/components/AuthLayout/index.tsx","../src/components/AppleSignInButton/index.tsx","../src/components/EmailInput/index.tsx","../src/components/SignInForm/index.tsx","../src/components/SignUpForm/index.tsx","../src/components/RememberMeCheckbox/index.tsx","../../../node_modules/lucide-react/dist/esm/shared/src/utils.js","../../../node_modules/lucide-react/dist/esm/defaultAttributes.js","../../../node_modules/lucide-react/dist/esm/Icon.js","../../../node_modules/lucide-react/dist/esm/createLucideIcon.js","../../../node_modules/lucide-react/dist/esm/icons/eye-off.js","../../../node_modules/lucide-react/dist/esm/icons/eye.js","../src/components/PasswordInput/index.tsx","../src/components/ResendCodeButton/index.tsx"],"sourcesContent":["import * as React from \"react\"\nimport {Link, useLocation } from \"@rpcbase/router\"\n\n\nconst LINKS_REDIRECTION_MAP = {\n \"/auth/sign-in\": {\n \"title\": \"Sign Up\",\n \"location\": \"/auth/sign-up\"\n },\n \"/auth/sign-up\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n },\n \"/auth/forgot-password\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n },\n \"/auth/logout-success\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n }\n}\n\ntype Pathname = keyof typeof LINKS_REDIRECTION_MAP\n\nexport const AuthLayout = ({\n sidePanel = null,\n children,\n}: {\n sidePanel: React.ReactElement | null\n children: React.ReactNode\n}) => {\n const location = useLocation()\n\n const linkTitle = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.title\n const linkLocation = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.location\n\n return (\n <div className=\"container relative hidden h-dvh flex-col items-center justify-center md:grid md:w-full lg:max-w-none lg:grid-cols-2 lg:px-0\">\n {/* Top-right redirection link */}\n <Link\n className=\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2 absolute right-4 top-4 md:right-8 md:top-8\"\n to={linkLocation}\n >\n {linkTitle}\n </Link>\n <div className=\"relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex\">\n {sidePanel}\n </div>\n <div className=\"mx-auto flex w-full flex-col justify-center gap-6 /*sm:w-[350px]*/\">\n {children}\n </div>\n </div>\n )\n}\n","import clsx from \"clsx\"\n\n\nexport const AppleSignInButton = ({\n onPress,\n className,\n}: {\n onPress: () => void;\n className?: string;\n}) => {\n\n return (\n <button\n onClick={onPress}\n className={clsx(`\n w-full\n bg-black text-white hover:bg-gray-800\n flex items-center justify-center\n px-6 py-2\n rounded-lg\n font-medium\n transition duration-150 ease-in-out\n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black\n `, className)}\n >\n <svg\n className=\"w-5 h-5 mr-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"white\"\n >\n <path d=\"M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.539 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701\" />\n </svg>\n <span>Continue with Apple</span>\n </button>\n )\n}\n","import {useFormContext} from \"@rpcbase/form\"\n\n\nexport const EmailInput = ({\n id,\n className,\n placeholder,\n}: {\n id: string\n className?: string\n placeholder?: string\n}) => {\n const {register, formState} = useFormContext()\n\n const error = formState.errors.email\n let errorMessage: string | undefined\n if (typeof error === \"string\") {\n errorMessage = error\n } else if (error && typeof error.message === \"string\") {\n errorMessage = error.message\n }\n\n return (\n <>\n <input\n id={id}\n type=\"email\"\n required\n autoComplete=\"email\"\n className={className}\n placeholder={placeholder}\n {...register(\"email\")}\n />\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","import { ReactNode } from \"react\"\nimport { useNavigate, useSearchParams } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type { SubmitHandler } from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-in\"\n\n\ntype SignInFormValues = z.input<typeof requestSchema>\n\nconst hasUnsafeNextPathChars = (value: string) => {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i)\n if (code === 92 || code <= 31 || code === 127) return true\n }\n return false\n}\n\nconst sanitizeNextPath = (raw: string | null) => {\n if (!raw) return null\n\n const nextPath = raw.trim()\n if (!nextPath) return null\n\n if (!nextPath.startsWith(\"/\") || nextPath.startsWith(\"//\")) {\n return null\n }\n\n if (hasUnsafeNextPathChars(nextPath)) {\n return null\n }\n\n try {\n const base = new URL(\"http://localhost\")\n const url = new URL(nextPath, base)\n if (url.origin !== base.origin) {\n return null\n }\n return `${url.pathname}${url.search}${url.hash}`\n } catch {\n return null\n }\n}\n\nexport const SignInForm = ({\n children,\n className\n}: {\n children: ReactNode,\n className?: string\n}) => {\n const navigate = useNavigate()\n const [searchParams] = useSearchParams()\n\n const methods = useForm<SignInFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignInFormValues> = async (data) => {\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-in\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json().catch(() => null) as { success?: boolean; error?: string } | null\n\n if (!res.ok || !json?.success) {\n const message = json?.error === \"invalid_credentials\"\n ? \"Invalid email or password.\"\n : \"Sign-in failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n const nextPath = sanitizeNextPath(searchParams.get(\"next\")) ?? \"/\"\n navigate(nextPath, { replace: true })\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import {ReactNode, useState} from \"react\"\nimport { useNavigate } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type {SubmitHandler} from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-up\"\n\n\ntype SignUpFormValues = z.input<typeof requestSchema>\n\n\nexport const SignUpForm = ({\n children,\n className,\n otpNextPath\n}: {\n children: ReactNode,\n className?: string\n otpNextPath?: string\n}) => {\n\n const navigate = useNavigate()\n\n const [serverMessage, setServerMessage] = useState<string | null>(null)\n const methods = useForm<SignUpFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignUpFormValues> = async (data) => {\n setServerMessage(null)\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-up\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json()\n\n if (!res.ok) {\n const message = json.error === \"user_exists\"\n ? \"An account already exists with this email.\"\n : \"Sign-up failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n if (!json.success) {\n methods.setError(\"root\", { type: \"server\", message: \"Sign-up failed. Please try again.\" })\n return\n }\n\n const search = new URLSearchParams({ email: data.email })\n if (otpNextPath) {\n search.set(\"next\", otpNextPath)\n }\n navigate(`/auth/sign-up-otp?${search.toString()}`)\n setServerMessage(\"Account created. Check your inbox to verify your email.\")\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n {serverMessage && (\n <p className=\"mt-2 text-sm text-green-700\" role=\"status\">\n {serverMessage}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import {ComponentType} from \"react\"\nimport {useFormContext} from \"@rpcbase/form\"\n\n\nexport const RememberMeCheckbox = ({\n label,\n as: Component = \"input\"\n}: {\n label: string,\n as?: ComponentType<any> | string\n}) => {\n const {register} = useFormContext()\n\n return (\n <>\n <Component\n id=\"rememberMe\"\n {...register(\"rememberMe\")}\n />\n <label\n htmlFor={\"rememberMe\"}\n className=\"pl-2 block text-sm/6 text-gray-900\"\n >\n {label}\n </label>\n </>\n )\n}\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\nconst hasA11yProp = (props) => {\n for (const prop in props) {\n if (prop.startsWith(\"aria-\") || prop === \"role\" || prop === \"title\") {\n return true;\n }\n }\n};\n\nexport { hasA11yProp, mergeClasses, toCamelCase, toKebabCase, toPascalCase };\n//# sourceMappingURL=utils.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes.js';\nimport { mergeClasses, hasA11yProp } from './shared/src/utils.js';\n\nconst Icon = forwardRef(\n ({\n color = \"currentColor\",\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = \"\",\n children,\n iconNode,\n ...rest\n }, ref) => createElement(\n \"svg\",\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,\n className: mergeClasses(\"lucide\", className),\n ...!children && !hasA11yProp(rest) && { \"aria-hidden\": \"true\" },\n ...rest\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...Array.isArray(children) ? children : [children]\n ]\n )\n);\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport { mergeClasses, toKebabCase, toPascalCase } from './shared/src/utils.js';\nimport Icon from './Icon.js';\n\nconst createLucideIcon = (iconName, iconNode) => {\n const Component = forwardRef(\n ({ className, ...props }, ref) => createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className\n ),\n ...props\n })\n );\n Component.displayName = toPascalCase(iconName);\n return Component;\n};\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",\n key: \"ct8e1f\"\n }\n ],\n [\"path\", { d: \"M14.084 14.158a3 3 0 0 1-4.242-4.242\", key: \"151rxh\" }],\n [\n \"path\",\n {\n d: \"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",\n key: \"13bj9a\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n];\nconst EyeOff = createLucideIcon(\"eye-off\", __iconNode);\n\nexport { __iconNode, EyeOff as default };\n//# sourceMappingURL=eye-off.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Eye = createLucideIcon(\"eye\", __iconNode);\n\nexport { __iconNode, Eye as default };\n//# sourceMappingURL=eye.js.map\n","import { useState } from \"react\"\nimport { Eye, EyeOff } from \"lucide-react\"\nimport { useFormContext } from \"@rpcbase/form\"\n\n\ntype PasswordInputProps = {\n id: string\n name?: string\n className?: string\n placeholder?: string\n autoComplete?: string\n}\n\nexport const PasswordInput = ({\n id,\n name = \"password\",\n className,\n placeholder,\n autoComplete = \"current-password\",\n}: PasswordInputProps) => {\n const [showPassword, setShowPassword] = useState(false)\n const { register, formState } = useFormContext()\n\n const fieldError = formState.errors[name as keyof typeof formState.errors]\n const errorMessage =\n typeof fieldError === \"string\"\n ? fieldError\n : typeof fieldError?.message === \"string\"\n ? fieldError.message\n : undefined\n\n const inputClassName = className ? `${className} pr-11` : \"pr-11\"\n\n return (\n <>\n <div className=\"relative\">\n <input\n id={id}\n type={showPassword ? \"text\" : \"password\"}\n autoComplete={autoComplete}\n className={inputClassName}\n placeholder={placeholder}\n {...register(name as any)}\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500\"\n onClick={() => setShowPassword((prev) => !prev)}\n aria-label={showPassword ? \"Hide characters\" : \"Show characters\"}\n >\n {showPassword ? (\n <EyeOff className=\"h-5 w-5\" aria-hidden />\n ) : (\n <Eye className=\"h-5 w-5\" aria-hidden />\n )}\n </button>\n </div>\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","import {useCallback, useEffect, useMemo, useState} from \"react\"\nimport clsx from \"clsx\"\n\n\nexport const useResendCountdown = (seconds = 60) => {\n const [remaining, setRemaining] = useState(seconds)\n const [isCountingDown, setIsCountingDown] = useState(true)\n\n useEffect(() => {\n if (!isCountingDown) return\n\n const timer = setInterval(() => {\n setRemaining((prev) => {\n if (prev <= 1) {\n setIsCountingDown(false)\n return seconds\n }\n return prev - 1\n })\n }, 1000)\n\n return () => clearInterval(timer)\n }, [isCountingDown, seconds])\n\n const restart = useCallback((nextSeconds?: number) => {\n const value = typeof nextSeconds === \"number\" ? nextSeconds : seconds\n setRemaining(value)\n setIsCountingDown(true)\n }, [seconds])\n\n const formatted = useMemo(() => {\n const minutes = Math.floor(remaining / 60)\n const secs = remaining % 60\n return `${minutes}:${secs.toString().padStart(2, \"0\")}`\n }, [remaining])\n\n const canResend = !isCountingDown\n\n return {remaining, formatted, isCountingDown, canResend, restart}\n}\n\n\nexport const ResendCodeButton = ({\n seconds = 60,\n onResend,\n className,\n disabled,\n}: {\n seconds?: number\n onResend?: () => Promise<void> | void\n className?: string\n disabled?: boolean\n}) => {\n const [isSending, setIsSending] = useState(false)\n const {formatted, isCountingDown, restart, canResend} = useResendCountdown(seconds)\n\n const handleClick = async () => {\n if (!canResend || isSending || disabled) return\n\n try {\n setIsSending(true)\n if (onResend) {\n await onResend()\n }\n } finally {\n restart()\n setIsSending(false)\n }\n }\n\n const isDisabled = disabled || isCountingDown || isSending\n const showCountdown = isDisabled && (isCountingDown || isSending)\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={isDisabled}\n aria-label=\"Resend code\"\n className={clsx(\n \"text-sm font-semibold text-sky-600 hover:underline disabled:cursor-not-allowed disabled:text-gray-400\",\n className\n )}\n >\n Resend code\n </button>\n {showCountdown ? (\n <span\n data-testid=\"resend-countdown\"\n className=\"text-sm text-gray-500\"\n aria-live=\"polite\"\n >\n ({formatted})\n </span>\n ) : null}\n </span>\n )\n}\n"],"names":["requestSchema","__iconNode"],"mappings":"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","x_google_ignoreList":[6,7,8,9,10,11]}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/components/AuthLayout/index.tsx","../src/components/AppleSignInButton/index.tsx","../src/components/EmailInput/index.tsx","../src/utils/sanitizeNextPath.ts","../src/components/SignInForm/index.tsx","../src/components/SignUpForm/index.tsx","../src/components/RememberMeCheckbox/index.tsx","../../../node_modules/lucide-react/dist/esm/shared/src/utils.js","../../../node_modules/lucide-react/dist/esm/defaultAttributes.js","../../../node_modules/lucide-react/dist/esm/Icon.js","../../../node_modules/lucide-react/dist/esm/createLucideIcon.js","../../../node_modules/lucide-react/dist/esm/icons/eye-off.js","../../../node_modules/lucide-react/dist/esm/icons/eye.js","../src/components/PasswordInput/index.tsx","../src/components/ResendCodeButton/index.tsx"],"sourcesContent":["import * as React from \"react\"\nimport {Link, useLocation } from \"@rpcbase/router\"\n\n\nconst LINKS_REDIRECTION_MAP = {\n \"/auth/sign-in\": {\n \"title\": \"Sign Up\",\n \"location\": \"/auth/sign-up\"\n },\n \"/auth/sign-up\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n },\n \"/auth/forgot-password\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n },\n \"/auth/logout-success\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n }\n}\n\ntype Pathname = keyof typeof LINKS_REDIRECTION_MAP\n\nexport const AuthLayout = ({\n sidePanel = null,\n children,\n}: {\n sidePanel: React.ReactElement | null\n children: React.ReactNode\n}) => {\n const location = useLocation()\n\n const linkTitle = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.title\n const linkLocation = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.location\n\n return (\n <div className=\"container relative hidden h-dvh flex-col items-center justify-center md:grid md:w-full lg:max-w-none lg:grid-cols-2 lg:px-0\">\n {/* Top-right redirection link */}\n {linkTitle && linkLocation ? (\n <Link\n className=\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2 absolute right-4 top-4 md:right-8 md:top-8\"\n to={linkLocation}\n >\n {linkTitle}\n </Link>\n ) : null}\n <div className=\"relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex\">\n {sidePanel}\n </div>\n <div className=\"mx-auto flex w-full flex-col justify-center gap-6 /*sm:w-[350px]*/\">\n {children}\n </div>\n </div>\n )\n}\n","import clsx from \"clsx\"\n\n\nexport const AppleSignInButton = ({\n onPress,\n className,\n}: {\n onPress: () => void;\n className?: string;\n}) => {\n\n return (\n <button\n onClick={onPress}\n className={clsx(`\n w-full\n bg-black text-white hover:bg-gray-800\n flex items-center justify-center\n px-6 py-2\n rounded-lg\n font-medium\n transition duration-150 ease-in-out\n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black\n `, className)}\n >\n <svg\n className=\"w-5 h-5 mr-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"white\"\n >\n <path d=\"M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.539 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701\" />\n </svg>\n <span>Continue with Apple</span>\n </button>\n )\n}\n","import {useFormContext} from \"@rpcbase/form\"\n\n\nexport const EmailInput = ({\n id,\n className,\n placeholder,\n}: {\n id: string\n className?: string\n placeholder?: string\n}) => {\n const {register, formState} = useFormContext()\n\n const error = formState.errors.email\n let errorMessage: string | undefined\n if (typeof error === \"string\") {\n errorMessage = error\n } else if (error && typeof error.message === \"string\") {\n errorMessage = error.message\n }\n\n return (\n <>\n <input\n id={id}\n type=\"email\"\n required\n autoComplete=\"email\"\n className={className}\n placeholder={placeholder}\n {...register(\"email\")}\n />\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","const hasUnsafeNextPathChars = (value: string) => {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i)\n if (code === 92 || code <= 31 || code === 127) return true\n }\n return false\n}\n\nexport const sanitizeNextPath = (raw: string | null) => {\n if (!raw) return null\n\n const nextPath = raw.trim()\n if (!nextPath) return null\n\n if (!nextPath.startsWith(\"/\") || nextPath.startsWith(\"//\")) {\n return null\n }\n\n if (hasUnsafeNextPathChars(nextPath)) {\n return null\n }\n\n try {\n const base = new URL(\"http://localhost\")\n const url = new URL(nextPath, base)\n if (url.origin !== base.origin) {\n return null\n }\n return `${url.pathname}${url.search}${url.hash}`\n } catch {\n return null\n }\n}\n\n","import { ReactNode } from \"react\"\nimport { useNavigate, useSearchParams } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type { SubmitHandler } from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-in\"\nimport { sanitizeNextPath } from \"../../utils/sanitizeNextPath\"\n\n\ntype SignInFormValues = z.input<typeof requestSchema>\n\nexport const SignInForm = ({\n children,\n className\n}: {\n children: ReactNode,\n className?: string\n}) => {\n const navigate = useNavigate()\n const [searchParams] = useSearchParams()\n\n const methods = useForm<SignInFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignInFormValues> = async (data) => {\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-in\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json().catch(() => null) as { success?: boolean; error?: string } | null\n\n if (!res.ok || !json?.success) {\n const message = json?.error === \"invalid_credentials\"\n ? \"Invalid email or password.\"\n : \"Sign-in failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n const nextPath = sanitizeNextPath(searchParams.get(\"next\")) ?? \"/\"\n navigate(nextPath, { replace: true })\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import {ReactNode, useState} from \"react\"\nimport { useNavigate } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type {SubmitHandler} from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-up\"\n\n\ntype SignUpFormValues = z.input<typeof requestSchema>\n\n\nexport const SignUpForm = ({\n children,\n className,\n otpNextPath\n}: {\n children: ReactNode,\n className?: string\n otpNextPath?: string\n}) => {\n\n const navigate = useNavigate()\n\n const [serverMessage, setServerMessage] = useState<string | null>(null)\n const methods = useForm<SignUpFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignUpFormValues> = async (data) => {\n setServerMessage(null)\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-up\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json()\n\n if (!res.ok) {\n const message = json.error === \"user_exists\"\n ? \"An account already exists with this email.\"\n : \"Sign-up failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n if (!json.success) {\n methods.setError(\"root\", { type: \"server\", message: \"Sign-up failed. Please try again.\" })\n return\n }\n\n const search = new URLSearchParams({ email: data.email })\n if (otpNextPath) {\n search.set(\"next\", otpNextPath)\n }\n navigate(`/auth/sign-up-otp?${search.toString()}`)\n setServerMessage(\"Account created. Check your inbox to verify your email.\")\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n {serverMessage && (\n <p className=\"mt-2 text-sm text-green-700\" role=\"status\">\n {serverMessage}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import {ComponentType} from \"react\"\nimport {useFormContext} from \"@rpcbase/form\"\n\n\nexport const RememberMeCheckbox = ({\n label,\n as: Component = \"input\"\n}: {\n label: string,\n as?: ComponentType<any> | string\n}) => {\n const {register} = useFormContext()\n\n return (\n <>\n <Component\n id=\"rememberMe\"\n {...register(\"rememberMe\")}\n />\n <label\n htmlFor={\"rememberMe\"}\n className=\"pl-2 block text-sm/6 text-gray-900\"\n >\n {label}\n </label>\n </>\n )\n}\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\nconst hasA11yProp = (props) => {\n for (const prop in props) {\n if (prop.startsWith(\"aria-\") || prop === \"role\" || prop === \"title\") {\n return true;\n }\n }\n};\n\nexport { hasA11yProp, mergeClasses, toCamelCase, toKebabCase, toPascalCase };\n//# sourceMappingURL=utils.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes.js';\nimport { mergeClasses, hasA11yProp } from './shared/src/utils.js';\n\nconst Icon = forwardRef(\n ({\n color = \"currentColor\",\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = \"\",\n children,\n iconNode,\n ...rest\n }, ref) => createElement(\n \"svg\",\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,\n className: mergeClasses(\"lucide\", className),\n ...!children && !hasA11yProp(rest) && { \"aria-hidden\": \"true\" },\n ...rest\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...Array.isArray(children) ? children : [children]\n ]\n )\n);\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport { mergeClasses, toKebabCase, toPascalCase } from './shared/src/utils.js';\nimport Icon from './Icon.js';\n\nconst createLucideIcon = (iconName, iconNode) => {\n const Component = forwardRef(\n ({ className, ...props }, ref) => createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className\n ),\n ...props\n })\n );\n Component.displayName = toPascalCase(iconName);\n return Component;\n};\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",\n key: \"ct8e1f\"\n }\n ],\n [\"path\", { d: \"M14.084 14.158a3 3 0 0 1-4.242-4.242\", key: \"151rxh\" }],\n [\n \"path\",\n {\n d: \"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",\n key: \"13bj9a\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n];\nconst EyeOff = createLucideIcon(\"eye-off\", __iconNode);\n\nexport { __iconNode, EyeOff as default };\n//# sourceMappingURL=eye-off.js.map\n","/**\n * @license lucide-react v0.562.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Eye = createLucideIcon(\"eye\", __iconNode);\n\nexport { __iconNode, Eye as default };\n//# sourceMappingURL=eye.js.map\n","import { useState } from \"react\"\nimport { Eye, EyeOff } from \"lucide-react\"\nimport { useFormContext } from \"@rpcbase/form\"\n\n\ntype PasswordInputProps = {\n id: string\n name?: string\n className?: string\n placeholder?: string\n autoComplete?: string\n}\n\nexport const PasswordInput = ({\n id,\n name = \"password\",\n className,\n placeholder,\n autoComplete = \"current-password\",\n}: PasswordInputProps) => {\n const [showPassword, setShowPassword] = useState(false)\n const { register, formState } = useFormContext()\n\n const fieldError = formState.errors[name as keyof typeof formState.errors]\n const errorMessage =\n typeof fieldError === \"string\"\n ? fieldError\n : typeof fieldError?.message === \"string\"\n ? fieldError.message\n : undefined\n\n const inputClassName = className ? `${className} pr-11` : \"pr-11\"\n\n return (\n <>\n <div className=\"relative\">\n <input\n id={id}\n type={showPassword ? \"text\" : \"password\"}\n autoComplete={autoComplete}\n className={inputClassName}\n placeholder={placeholder}\n {...register(name as any)}\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500\"\n onClick={() => setShowPassword((prev) => !prev)}\n aria-label={showPassword ? \"Hide characters\" : \"Show characters\"}\n >\n {showPassword ? (\n <EyeOff className=\"h-5 w-5\" aria-hidden />\n ) : (\n <Eye className=\"h-5 w-5\" aria-hidden />\n )}\n </button>\n </div>\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","import {useCallback, useEffect, useMemo, useState} from \"react\"\nimport clsx from \"clsx\"\n\n\nexport const useResendCountdown = (seconds = 60) => {\n const [remaining, setRemaining] = useState(seconds)\n const [isCountingDown, setIsCountingDown] = useState(true)\n\n useEffect(() => {\n if (!isCountingDown) return\n\n const timer = setInterval(() => {\n setRemaining((prev) => {\n if (prev <= 1) {\n setIsCountingDown(false)\n return seconds\n }\n return prev - 1\n })\n }, 1000)\n\n return () => clearInterval(timer)\n }, [isCountingDown, seconds])\n\n const restart = useCallback((nextSeconds?: number) => {\n const value = typeof nextSeconds === \"number\" ? nextSeconds : seconds\n setRemaining(value)\n setIsCountingDown(true)\n }, [seconds])\n\n const formatted = useMemo(() => {\n const minutes = Math.floor(remaining / 60)\n const secs = remaining % 60\n return `${minutes}:${secs.toString().padStart(2, \"0\")}`\n }, [remaining])\n\n const canResend = !isCountingDown\n\n return {remaining, formatted, isCountingDown, canResend, restart}\n}\n\n\nexport const ResendCodeButton = ({\n seconds = 60,\n onResend,\n className,\n disabled,\n}: {\n seconds?: number\n onResend?: () => Promise<void> | void\n className?: string\n disabled?: boolean\n}) => {\n const [isSending, setIsSending] = useState(false)\n const {formatted, isCountingDown, restart, canResend} = useResendCountdown(seconds)\n\n const handleClick = async () => {\n if (!canResend || isSending || disabled) return\n\n try {\n setIsSending(true)\n if (onResend) {\n await onResend()\n }\n } finally {\n restart()\n setIsSending(false)\n }\n }\n\n const isDisabled = disabled || isCountingDown || isSending\n const showCountdown = isDisabled && (isCountingDown || isSending)\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={isDisabled}\n aria-label=\"Resend code\"\n className={clsx(\n \"text-sm font-semibold text-sky-600 hover:underline disabled:cursor-not-allowed disabled:text-gray-400\",\n className\n )}\n >\n Resend code\n </button>\n {showCountdown ? (\n <span\n data-testid=\"resend-countdown\"\n className=\"text-sm text-gray-500\"\n aria-live=\"polite\"\n >\n ({formatted})\n </span>\n ) : null}\n </span>\n )\n}\n"],"names":["requestSchema","__iconNode"],"mappings":"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","x_google_ignoreList":[7,8,9,10,11,12]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type OAuthProviderConfig = {
|
|
2
|
+
issuer: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret?: string;
|
|
5
|
+
scope?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const configureOAuthProviders: (providers: Record<string, OAuthProviderConfig>) => void;
|
|
8
|
+
export declare const setOAuthProviders: (providers: Record<string, OAuthProviderConfig>) => void;
|
|
9
|
+
export declare const getOAuthProviderConfig: (providerId: string) => OAuthProviderConfig | null;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/oauth/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAoDD,eAAO,MAAM,uBAAuB,GAAI,WAAW,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAIrF,CAAA;AAED,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAG/E,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,YAAY,MAAM,KAAG,mBAAmB,GAAG,IAGjF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/oauth/jwt.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,GAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,MAAM,KAAG,CAAC,GAAG,IAYjF,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type OidcWellKnown = {
|
|
2
|
+
issuer: string;
|
|
3
|
+
authorization_endpoint: string;
|
|
4
|
+
token_endpoint: string;
|
|
5
|
+
userinfo_endpoint?: string;
|
|
6
|
+
jwks_uri?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const getOidcWellKnown: (issuerRaw: string) => Promise<OidcWellKnown>;
|
|
9
|
+
//# sourceMappingURL=oidc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../../src/oauth/oidc.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,sBAAsB,EAAE,MAAM,CAAA;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAMD,eAAO,MAAM,gBAAgB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,aAAa,CAuD/E,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../src/oauth/pkce.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,WAIvB,CAAA;AAEtB,eAAO,MAAM,gBAAgB;;;CAK5B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providerId.d.ts","sourceRoot":"","sources":["../../src/oauth/providerId.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,YAMlD,CAAA"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2
|
+
const normalizeIssuer = (raw) => raw.trim().replace(/\/+$/, "");
|
|
3
|
+
const getOidcWellKnown = async (issuerRaw) => {
|
|
4
|
+
const issuer = normalizeIssuer(issuerRaw);
|
|
5
|
+
if (!issuer) {
|
|
6
|
+
throw new Error("OIDC issuer is required");
|
|
7
|
+
}
|
|
8
|
+
const existing = cache.get(issuer);
|
|
9
|
+
if (existing) {
|
|
10
|
+
return existing;
|
|
11
|
+
}
|
|
12
|
+
const loader = (async () => {
|
|
13
|
+
const url = new URL("/.well-known/openid-configuration", issuer);
|
|
14
|
+
const response = await fetch(url, { headers: { Accept: "application/json" } });
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
const body = await response.text().catch(() => "");
|
|
17
|
+
throw new Error(`Failed to fetch OIDC discovery document: ${response.status} ${body}`);
|
|
18
|
+
}
|
|
19
|
+
const json = await response.json().catch(() => null);
|
|
20
|
+
if (!json || typeof json !== "object") {
|
|
21
|
+
throw new Error("Invalid OIDC discovery document");
|
|
22
|
+
}
|
|
23
|
+
const authorizationEndpoint = typeof json.authorization_endpoint === "string" ? json.authorization_endpoint : "";
|
|
24
|
+
const tokenEndpoint = typeof json.token_endpoint === "string" ? json.token_endpoint : "";
|
|
25
|
+
const issuerValue = typeof json.issuer === "string" ? json.issuer : issuer;
|
|
26
|
+
const userinfoEndpoint = typeof json.userinfo_endpoint === "string" ? json.userinfo_endpoint : void 0;
|
|
27
|
+
const jwksUri = typeof json.jwks_uri === "string" ? json.jwks_uri : void 0;
|
|
28
|
+
if (!authorizationEndpoint || !tokenEndpoint) {
|
|
29
|
+
throw new Error("OIDC discovery document missing required endpoints");
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
issuer: issuerValue,
|
|
33
|
+
authorization_endpoint: authorizationEndpoint,
|
|
34
|
+
token_endpoint: tokenEndpoint,
|
|
35
|
+
userinfo_endpoint: userinfoEndpoint,
|
|
36
|
+
jwks_uri: jwksUri
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
cache.set(issuer, loader);
|
|
40
|
+
try {
|
|
41
|
+
return await loader;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
cache.delete(issuer);
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const RESERVED_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
48
|
+
const isSafeOAuthProviderId = (value) => {
|
|
49
|
+
const providerId = value.trim();
|
|
50
|
+
if (!providerId) return false;
|
|
51
|
+
if (providerId.length > 128) return false;
|
|
52
|
+
if (RESERVED_KEYS.has(providerId)) return false;
|
|
53
|
+
return /^[a-zA-Z0-9_-]+$/.test(providerId);
|
|
54
|
+
};
|
|
55
|
+
export {
|
|
56
|
+
getOidcWellKnown as g,
|
|
57
|
+
isSafeOAuthProviderId as i
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=providerId-Clk38lTZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providerId-Clk38lTZ.js","sources":["../src/oauth/oidc.ts","../src/oauth/providerId.ts"],"sourcesContent":["export type OidcWellKnown = {\n issuer: string\n authorization_endpoint: string\n token_endpoint: string\n userinfo_endpoint?: string\n jwks_uri?: string\n}\n\nconst cache = new Map<string, Promise<OidcWellKnown>>()\n\nconst normalizeIssuer = (raw: string) => raw.trim().replace(/\\/+$/, \"\")\n\nexport const getOidcWellKnown = async (issuerRaw: string): Promise<OidcWellKnown> => {\n const issuer = normalizeIssuer(issuerRaw)\n if (!issuer) {\n throw new Error(\"OIDC issuer is required\")\n }\n\n const existing = cache.get(issuer)\n if (existing) {\n return existing\n }\n\n const loader = (async () => {\n const url = new URL(\"/.well-known/openid-configuration\", issuer)\n const response = await fetch(url, { headers: { Accept: \"application/json\" } })\n if (!response.ok) {\n const body = await response.text().catch(() => \"\")\n throw new Error(`Failed to fetch OIDC discovery document: ${response.status} ${body}`)\n }\n\n const json = await response.json().catch(() => null) as Partial<OidcWellKnown> | null\n if (!json || typeof json !== \"object\") {\n throw new Error(\"Invalid OIDC discovery document\")\n }\n\n const authorizationEndpoint = typeof json.authorization_endpoint === \"string\"\n ? json.authorization_endpoint\n : \"\"\n const tokenEndpoint = typeof json.token_endpoint === \"string\"\n ? json.token_endpoint\n : \"\"\n const issuerValue = typeof json.issuer === \"string\" ? json.issuer : issuer\n const userinfoEndpoint = typeof json.userinfo_endpoint === \"string\" ? json.userinfo_endpoint : undefined\n const jwksUri = typeof json.jwks_uri === \"string\" ? json.jwks_uri : undefined\n\n if (!authorizationEndpoint || !tokenEndpoint) {\n throw new Error(\"OIDC discovery document missing required endpoints\")\n }\n\n return {\n issuer: issuerValue,\n authorization_endpoint: authorizationEndpoint,\n token_endpoint: tokenEndpoint,\n userinfo_endpoint: userinfoEndpoint,\n jwks_uri: jwksUri,\n } satisfies OidcWellKnown\n })()\n\n cache.set(issuer, loader)\n\n try {\n return await loader\n } catch (err) {\n cache.delete(issuer)\n throw err\n }\n}\n\n","const RESERVED_KEYS = new Set([\"__proto__\", \"prototype\", \"constructor\"])\n\nexport const isSafeOAuthProviderId = (value: string) => {\n const providerId = value.trim()\n if (!providerId) return false\n if (providerId.length > 128) return false\n if (RESERVED_KEYS.has(providerId)) return false\n return /^[a-zA-Z0-9_-]+$/.test(providerId)\n}\n\n"],"names":[],"mappings":"AAQA,MAAM,4BAAY,IAAA;AAElB,MAAM,kBAAkB,CAAC,QAAgB,IAAI,OAAO,QAAQ,QAAQ,EAAE;AAE/D,MAAM,mBAAmB,OAAO,cAA8C;AACnF,QAAM,SAAS,gBAAgB,SAAS;AACxC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAEA,QAAM,WAAW,MAAM,IAAI,MAAM;AACjC,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,YAAY;AAC1B,UAAM,MAAM,IAAI,IAAI,qCAAqC,MAAM;AAC/D,UAAM,WAAW,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,QAAQ,mBAAA,GAAsB;AAC7E,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,EAAE;AACjD,YAAM,IAAI,MAAM,4CAA4C,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvF;AAEA,UAAM,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,IAAI;AACnD,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,UAAM,wBAAwB,OAAO,KAAK,2BAA2B,WACjE,KAAK,yBACL;AACJ,UAAM,gBAAgB,OAAO,KAAK,mBAAmB,WACjD,KAAK,iBACL;AACJ,UAAM,cAAc,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AACpE,UAAM,mBAAmB,OAAO,KAAK,sBAAsB,WAAW,KAAK,oBAAoB;AAC/F,UAAM,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AAEpE,QAAI,CAAC,yBAAyB,CAAC,eAAe;AAC5C,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,wBAAwB;AAAA,MACxB,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,MACnB,UAAU;AAAA,IAAA;AAAA,EAEd,GAAA;AAEA,QAAM,IAAI,QAAQ,MAAM;AAExB,MAAI;AACF,WAAO,MAAM;AAAA,EACf,SAAS,KAAK;AACZ,UAAM,OAAO,MAAM;AACnB,UAAM;AAAA,EACR;AACF;ACnEA,MAAM,gBAAgB,oBAAI,IAAI,CAAC,aAAa,aAAa,aAAa,CAAC;AAEhE,MAAM,wBAAwB,CAAC,UAAkB;AACtD,QAAM,aAAa,MAAM,KAAA;AACzB,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI,WAAW,SAAS,IAAK,QAAO;AACpC,MAAI,cAAc,IAAI,UAAU,EAAG,QAAO;AAC1C,SAAO,mBAAmB,KAAK,UAAU;AAC3C;"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const STORE_KEY = /* @__PURE__ */ Symbol.for("@rpcbase/auth/oauthProviders");
|
|
2
|
+
const getStore = () => {
|
|
3
|
+
const anyGlobal = globalThis;
|
|
4
|
+
const existing = anyGlobal[STORE_KEY];
|
|
5
|
+
if (existing && typeof existing === "object" && existing.providers && typeof existing.providers === "object") {
|
|
6
|
+
return existing;
|
|
7
|
+
}
|
|
8
|
+
const created = { providers: {} };
|
|
9
|
+
anyGlobal[STORE_KEY] = created;
|
|
10
|
+
return created;
|
|
11
|
+
};
|
|
12
|
+
const normalizeProviders = (providers) => {
|
|
13
|
+
const result = {};
|
|
14
|
+
for (const [providerIdRaw, value] of Object.entries(providers)) {
|
|
15
|
+
const providerId = providerIdRaw.trim();
|
|
16
|
+
if (!providerId) continue;
|
|
17
|
+
const issuer = typeof value?.issuer === "string" ? value.issuer.trim() : "";
|
|
18
|
+
const clientId = typeof value?.clientId === "string" ? value.clientId.trim() : "";
|
|
19
|
+
if (!issuer || !clientId) continue;
|
|
20
|
+
const clientSecret = typeof value?.clientSecret === "string" && value.clientSecret.trim() ? value.clientSecret : void 0;
|
|
21
|
+
const scope = typeof value?.scope === "string" && value.scope.trim() ? value.scope : void 0;
|
|
22
|
+
result[providerId] = {
|
|
23
|
+
issuer,
|
|
24
|
+
clientId,
|
|
25
|
+
clientSecret,
|
|
26
|
+
scope
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
const configureOAuthProviders = (providers) => {
|
|
32
|
+
const store = getStore();
|
|
33
|
+
const normalized = normalizeProviders(providers);
|
|
34
|
+
store.providers = { ...store.providers, ...normalized };
|
|
35
|
+
};
|
|
36
|
+
const setOAuthProviders = (providers) => {
|
|
37
|
+
const store = getStore();
|
|
38
|
+
store.providers = normalizeProviders(providers);
|
|
39
|
+
};
|
|
40
|
+
const getOAuthProviderConfig = (providerId) => {
|
|
41
|
+
const store = getStore();
|
|
42
|
+
return store.providers[providerId] ?? null;
|
|
43
|
+
};
|
|
44
|
+
const routes = Object.entries({
|
|
45
|
+
.../* @__PURE__ */ Object.assign({ "./api/me/handler.ts": () => import("./handler-DvUXoOYN.js"), "./api/oauth/callback/handler.ts": () => import("./handler-n7Lp9zg7.js"), "./api/oauth/start/handler.ts": () => import("./handler-BXyRZsH5.js"), "./api/resend-otp/handler.ts": () => import("./handler-DJwOgqtP.js"), "./api/sign-in/handler.ts": () => import("./handler-CUtZEFQm.js"), "./api/sign-out/handler.ts": () => import("./handler-Zcu5rM1_.js"), "./api/sign-up/handler.ts": () => import("./handler-sUCM6Ee3.js"), "./api/verify-otp/handler.ts": () => import("./handler-DyqylA1h.js") })
|
|
46
|
+
}).reduce((acc, [path, mod]) => {
|
|
47
|
+
acc[path.replace("./api/", "@rpcbase/auth/api/")] = mod;
|
|
48
|
+
return acc;
|
|
49
|
+
}, {});
|
|
50
|
+
export {
|
|
51
|
+
configureOAuthProviders as c,
|
|
52
|
+
getOAuthProviderConfig as g,
|
|
53
|
+
routes as r,
|
|
54
|
+
setOAuthProviders as s
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=routes-ChxNWaaP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-ChxNWaaP.js","sources":["../src/oauth/config.ts","../src/routes.ts"],"sourcesContent":["export type OAuthProviderConfig = {\n issuer: string\n clientId: string\n clientSecret?: string\n scope?: string\n}\n\ntype OAuthProvidersConfig = Record<string, OAuthProviderConfig>\n\ntype OAuthProvidersStore = {\n providers: OAuthProvidersConfig\n}\n\nconst STORE_KEY = Symbol.for(\"@rpcbase/auth/oauthProviders\")\n\nconst getStore = (): OAuthProvidersStore => {\n const anyGlobal = globalThis as unknown as Record<string | symbol, unknown>\n const existing = anyGlobal[STORE_KEY] as OAuthProvidersStore | undefined\n if (existing && typeof existing === \"object\" && existing.providers && typeof existing.providers === \"object\") {\n return existing\n }\n\n const created: OAuthProvidersStore = { providers: {} }\n anyGlobal[STORE_KEY] = created\n return created\n}\n\nconst normalizeProviders = (providers: Record<string, OAuthProviderConfig>): OAuthProvidersConfig => {\n const result: OAuthProvidersConfig = {}\n\n for (const [providerIdRaw, value] of Object.entries(providers)) {\n const providerId = providerIdRaw.trim()\n if (!providerId) continue\n\n const issuer = typeof value?.issuer === \"string\" ? value.issuer.trim() : \"\"\n const clientId = typeof value?.clientId === \"string\" ? value.clientId.trim() : \"\"\n if (!issuer || !clientId) continue\n\n const clientSecret = typeof value?.clientSecret === \"string\" && value.clientSecret.trim()\n ? value.clientSecret\n : undefined\n\n const scope = typeof value?.scope === \"string\" && value.scope.trim()\n ? value.scope\n : undefined\n\n result[providerId] = {\n issuer,\n clientId,\n clientSecret,\n scope,\n }\n }\n\n return result\n}\n\nexport const configureOAuthProviders = (providers: Record<string, OAuthProviderConfig>) => {\n const store = getStore()\n const normalized = normalizeProviders(providers)\n store.providers = { ...store.providers, ...normalized }\n}\n\nexport const setOAuthProviders = (providers: Record<string, OAuthProviderConfig>) => {\n const store = getStore()\n store.providers = normalizeProviders(providers)\n}\n\nexport const getOAuthProviderConfig = (providerId: string): OAuthProviderConfig | null => {\n const store = getStore()\n return store.providers[providerId] ?? null\n}\n","export const routes = Object.entries({\n ...import.meta.glob(\"./**/handler.ts\"),\n}).reduce<Record<string, unknown>>((acc, [path, mod]) => {\n // re-add package name in front of the path so we know which are the framework routes vs app routes\n acc[path.replace(\"./api/\", \"@rpcbase/auth/api/\")] = mod\n return acc\n}, {})\n\nexport { configureOAuthProviders, setOAuthProviders } from \"./oauth/config\"\nexport type { OAuthProviderConfig } from \"./oauth/config\"\n"],"names":[],"mappings":"AAaA,MAAM,YAAY,uBAAO,IAAI,8BAA8B;AAE3D,MAAM,WAAW,MAA2B;AAC1C,QAAM,YAAY;AAClB,QAAM,WAAW,UAAU,SAAS;AACpC,MAAI,YAAY,OAAO,aAAa,YAAY,SAAS,aAAa,OAAO,SAAS,cAAc,UAAU;AAC5G,WAAO;AAAA,EACT;AAEA,QAAM,UAA+B,EAAE,WAAW,GAAC;AACnD,YAAU,SAAS,IAAI;AACvB,SAAO;AACT;AAEA,MAAM,qBAAqB,CAAC,cAAyE;AACnG,QAAM,SAA+B,CAAA;AAErC,aAAW,CAAC,eAAe,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC9D,UAAM,aAAa,cAAc,KAAA;AACjC,QAAI,CAAC,WAAY;AAEjB,UAAM,SAAS,OAAO,OAAO,WAAW,WAAW,MAAM,OAAO,SAAS;AACzE,UAAM,WAAW,OAAO,OAAO,aAAa,WAAW,MAAM,SAAS,SAAS;AAC/E,QAAI,CAAC,UAAU,CAAC,SAAU;AAE1B,UAAM,eAAe,OAAO,OAAO,iBAAiB,YAAY,MAAM,aAAa,KAAA,IAC/E,MAAM,eACN;AAEJ,UAAM,QAAQ,OAAO,OAAO,UAAU,YAAY,MAAM,MAAM,KAAA,IAC1D,MAAM,QACN;AAEJ,WAAO,UAAU,IAAI;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO;AACT;AAEO,MAAM,0BAA0B,CAAC,cAAmD;AACzF,QAAM,QAAQ,SAAA;AACd,QAAM,aAAa,mBAAmB,SAAS;AAC/C,QAAM,YAAY,EAAE,GAAG,MAAM,WAAW,GAAG,WAAA;AAC7C;AAEO,MAAM,oBAAoB,CAAC,cAAmD;AACnF,QAAM,QAAQ,SAAA;AACd,QAAM,YAAY,mBAAmB,SAAS;AAChD;AAEO,MAAM,yBAAyB,CAAC,eAAmD;AACxF,QAAM,QAAQ,SAAA;AACd,SAAO,MAAM,UAAU,UAAU,KAAK;AACxC;ACvEO,MAAM,SAAS,OAAO,QAAQ;AAAA,EACnC,GAAG,uBAAA,OAAA,EAAA,uBAAA,MAAA,OAAA,uBAAA,GAAA,mCAAA,MAAA,OAAA,uBAAA,GAAA,gCAAA,MAAA,OAAA,uBAAA,GAAA,+BAAA,MAAA,OAAA,uBAAA,GAAA,4BAAA,MAAA,OAAA,uBAAA,GAAA,6BAAA,MAAA,OAAA,uBAAA,GAAA,4BAAA,MAAA,OAAA,uBAAA,GAAA,+BAAA,MAAA,OAAA,uBAAA,EAAA,CAAA;AACL,CAAC,EAAE,OAAgC,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM;AAEvD,MAAI,KAAK,QAAQ,UAAU,oBAAoB,CAAC,IAAI;AACpD,SAAO;AACT,GAAG,CAAA,CAAE;"}
|
package/dist/routes.d.ts
CHANGED
package/dist/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,yBAMb,CAAA"}
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,yBAMb,CAAA;AAEN,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/routes.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
.../* @__PURE__ */ Object.assign({ "./api/me/handler.ts": () => import("./handler-DvUXoOYN.js"), "./api/resend-otp/handler.ts": () => import("./handler-DJwOgqtP.js"), "./api/sign-in/handler.ts": () => import("./handler-CUtZEFQm.js"), "./api/sign-out/handler.ts": () => import("./handler-Zcu5rM1_.js"), "./api/sign-up/handler.ts": () => import("./handler-sUCM6Ee3.js"), "./api/verify-otp/handler.ts": () => import("./handler-DyqylA1h.js") })
|
|
3
|
-
}).reduce((acc, [path, mod]) => {
|
|
4
|
-
acc[path.replace("./api/", "@rpcbase/auth/api/")] = mod;
|
|
5
|
-
return acc;
|
|
6
|
-
}, {});
|
|
1
|
+
import { c, r, s } from "./routes-ChxNWaaP.js";
|
|
7
2
|
export {
|
|
8
|
-
|
|
3
|
+
c as configureOAuthProviders,
|
|
4
|
+
r as routes,
|
|
5
|
+
s as setOAuthProviders
|
|
9
6
|
};
|
|
10
7
|
//# sourceMappingURL=routes.js.map
|
package/dist/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sources":[
|
|
1
|
+
{"version":3,"file":"routes.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeNextPath.d.ts","sourceRoot":"","sources":["../../src/utils/sanitizeNextPath.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,GAAG,IAAI,kBAwBlD,CAAA"}
|