@rpcbase/auth 0.89.0 → 0.91.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 +59 -12
- package/dist/oauth/config.d.ts +2 -1
- package/dist/oauth/config.d.ts.map +1 -1
- package/dist/oauth/index.d.ts +5 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +578 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/lib.d.ts +68 -0
- package/dist/oauth/lib.d.ts.map +1 -0
- package/dist/routes.d.ts +0 -2
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +7 -4
- package/dist/routes.js.map +1 -1
- package/package.json +6 -1
- package/dist/api/oauth/callback/handler.d.ts +0 -5
- package/dist/api/oauth/callback/handler.d.ts.map +0 -1
- package/dist/api/oauth/callback/index.d.ts +0 -10
- package/dist/api/oauth/callback/index.d.ts.map +0 -1
- package/dist/api/oauth/start/handler.d.ts +0 -5
- package/dist/api/oauth/start/handler.d.ts.map +0 -1
- package/dist/api/oauth/start/index.d.ts +0 -10
- package/dist/api/oauth/start/index.d.ts.map +0 -1
- package/dist/handler-BXyRZsH5.js +0 -81
- package/dist/handler-BXyRZsH5.js.map +0 -1
- package/dist/handler-n7Lp9zg7.js +0 -235
- package/dist/handler-n7Lp9zg7.js.map +0 -1
- package/dist/providerId-Clk38lTZ.js +0 -59
- package/dist/providerId-Clk38lTZ.js.map +0 -1
- package/dist/routes-ChxNWaaP.js +0 -56
- package/dist/routes-ChxNWaaP.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
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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"}
|
|
@@ -1,59 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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;"}
|
package/dist/routes-ChxNWaaP.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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;"}
|