kavachos 0.1.5 → 0.2.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/dist/a2a/index.d.ts +1 -1
- package/dist/agent/index.d.ts +2 -2
- package/dist/audit/index.d.ts +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +687 -758
- package/dist/auth/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +684 -651
- package/dist/index.js.map +1 -1
- package/dist/permission/index.d.ts +2 -2
- package/dist/{types-5Ua5KlPc.d.ts → types-B02D3kZy.d.ts} +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1798,7 +1798,7 @@ function createAdminModule(config, db, sessionManager) {
|
|
|
1798
1798
|
const url = new URL(request.url);
|
|
1799
1799
|
const { pathname } = url;
|
|
1800
1800
|
const { method } = request;
|
|
1801
|
-
const
|
|
1801
|
+
const json2 = (data, status = 200) => new Response(JSON.stringify(data), {
|
|
1802
1802
|
status,
|
|
1803
1803
|
headers: { "Content-Type": "application/json" }
|
|
1804
1804
|
});
|
|
@@ -1807,14 +1807,14 @@ function createAdminModule(config, db, sessionManager) {
|
|
|
1807
1807
|
const offset = url.searchParams.get("offset") ? Number(url.searchParams.get("offset")) : void 0;
|
|
1808
1808
|
const search = url.searchParams.get("search") ?? void 0;
|
|
1809
1809
|
const result = await listUsers({ limit, offset, search });
|
|
1810
|
-
return
|
|
1810
|
+
return json2(result);
|
|
1811
1811
|
}
|
|
1812
1812
|
const userMatch = /^\/auth\/admin\/users\/([^/]+)$/.exec(pathname);
|
|
1813
1813
|
if (method === "GET" && userMatch) {
|
|
1814
1814
|
const userId = decodeURIComponent(userMatch[1] ?? "");
|
|
1815
1815
|
const user = await getUser(userId);
|
|
1816
|
-
if (!user) return
|
|
1817
|
-
return
|
|
1816
|
+
if (!user) return json2({ error: "User not found" }, 404);
|
|
1817
|
+
return json2(user);
|
|
1818
1818
|
}
|
|
1819
1819
|
const banMatch = /^\/auth\/admin\/users\/([^/]+)\/ban$/.exec(pathname);
|
|
1820
1820
|
if (method === "POST" && banMatch) {
|
|
@@ -1827,19 +1827,19 @@ function createAdminModule(config, db, sessionManager) {
|
|
|
1827
1827
|
const reason = typeof body.reason === "string" ? body.reason : void 0;
|
|
1828
1828
|
const expiresAt = body.expiresAt ? new Date(body.expiresAt) : void 0;
|
|
1829
1829
|
await banUser(userId, reason, expiresAt);
|
|
1830
|
-
return
|
|
1830
|
+
return json2({ success: true });
|
|
1831
1831
|
}
|
|
1832
1832
|
const unbanMatch = /^\/auth\/admin\/users\/([^/]+)\/unban$/.exec(pathname);
|
|
1833
1833
|
if (method === "POST" && unbanMatch) {
|
|
1834
1834
|
const userId = decodeURIComponent(unbanMatch[1] ?? "");
|
|
1835
1835
|
await unbanUser(userId);
|
|
1836
|
-
return
|
|
1836
|
+
return json2({ success: true });
|
|
1837
1837
|
}
|
|
1838
1838
|
const deleteMatch = /^\/auth\/admin\/users\/([^/]+)$/.exec(pathname);
|
|
1839
1839
|
if (method === "DELETE" && deleteMatch) {
|
|
1840
1840
|
const userId = decodeURIComponent(deleteMatch[1] ?? "");
|
|
1841
1841
|
await deleteUser(userId);
|
|
1842
|
-
return
|
|
1842
|
+
return json2({ success: true });
|
|
1843
1843
|
}
|
|
1844
1844
|
const impersonateMatch = /^\/auth\/admin\/impersonate\/([^/]+)$/.exec(pathname);
|
|
1845
1845
|
if (method === "POST" && impersonateMatch) {
|
|
@@ -1848,28 +1848,28 @@ function createAdminModule(config, db, sessionManager) {
|
|
|
1848
1848
|
try {
|
|
1849
1849
|
body = await request.json();
|
|
1850
1850
|
} catch {
|
|
1851
|
-
return
|
|
1851
|
+
return json2({ error: "Invalid JSON body" }, 400);
|
|
1852
1852
|
}
|
|
1853
1853
|
const adminUserId = body.adminUserId;
|
|
1854
1854
|
if (typeof adminUserId !== "string") {
|
|
1855
|
-
return
|
|
1855
|
+
return json2({ error: "Missing required field: adminUserId" }, 400);
|
|
1856
1856
|
}
|
|
1857
1857
|
try {
|
|
1858
1858
|
const result = await impersonate(adminUserId, targetUserId);
|
|
1859
|
-
return
|
|
1859
|
+
return json2(result);
|
|
1860
1860
|
} catch (err2) {
|
|
1861
|
-
return
|
|
1861
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 403);
|
|
1862
1862
|
}
|
|
1863
1863
|
}
|
|
1864
1864
|
if (method === "POST" && pathname === "/auth/admin/stop-impersonation") {
|
|
1865
1865
|
const auth = request.headers.get("Authorization");
|
|
1866
1866
|
const token = auth?.startsWith("Bearer ") ? auth.slice(7) : null;
|
|
1867
|
-
if (!token) return
|
|
1867
|
+
if (!token) return json2({ error: "Missing Authorization header" }, 401);
|
|
1868
1868
|
try {
|
|
1869
1869
|
await stopImpersonation(token);
|
|
1870
|
-
return
|
|
1870
|
+
return json2({ success: true });
|
|
1871
1871
|
} catch (err2) {
|
|
1872
|
-
return
|
|
1872
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 400);
|
|
1873
1873
|
}
|
|
1874
1874
|
}
|
|
1875
1875
|
return null;
|
|
@@ -1887,78 +1887,9 @@ function createAdminModule(config, db, sessionManager) {
|
|
|
1887
1887
|
handleRequest
|
|
1888
1888
|
};
|
|
1889
1889
|
}
|
|
1890
|
-
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
|
|
1891
|
-
function createSessionManager(config, db) {
|
|
1892
|
-
if (!config.secret || config.secret.length < 32) {
|
|
1893
|
-
throw new Error("SessionManager: secret must be at least 32 characters.");
|
|
1894
|
-
}
|
|
1895
|
-
const maxAge = config.maxAge ?? DEFAULT_MAX_AGE_SECONDS;
|
|
1896
|
-
const keyObject = new TextEncoder().encode(config.secret);
|
|
1897
|
-
function rowToSession2(row) {
|
|
1898
|
-
return {
|
|
1899
|
-
id: row.id,
|
|
1900
|
-
userId: row.userId,
|
|
1901
|
-
expiresAt: row.expiresAt,
|
|
1902
|
-
createdAt: row.createdAt,
|
|
1903
|
-
...row.metadata !== null && { metadata: row.metadata }
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
|
-
async function create(userId, metadata) {
|
|
1907
|
-
const id = generateId();
|
|
1908
|
-
const now = /* @__PURE__ */ new Date();
|
|
1909
|
-
const expiresAt = new Date(now.getTime() + maxAge * 1e3);
|
|
1910
|
-
await db.insert(sessions).values({
|
|
1911
|
-
id,
|
|
1912
|
-
userId,
|
|
1913
|
-
expiresAt,
|
|
1914
|
-
metadata: metadata ?? null,
|
|
1915
|
-
createdAt: now
|
|
1916
|
-
});
|
|
1917
|
-
const token = await new SignJWT({ sub: id }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(Math.floor(expiresAt.getTime() / 1e3)).sign(keyObject);
|
|
1918
|
-
const session = {
|
|
1919
|
-
id,
|
|
1920
|
-
userId,
|
|
1921
|
-
expiresAt,
|
|
1922
|
-
createdAt: now,
|
|
1923
|
-
...metadata !== void 0 && { metadata }
|
|
1924
|
-
};
|
|
1925
|
-
return { session, token };
|
|
1926
|
-
}
|
|
1927
|
-
async function validate(token) {
|
|
1928
|
-
let sessionId;
|
|
1929
|
-
try {
|
|
1930
|
-
const { payload } = await jwtVerify(token, keyObject);
|
|
1931
|
-
if (typeof payload.sub !== "string" || !payload.sub) return null;
|
|
1932
|
-
sessionId = payload.sub;
|
|
1933
|
-
} catch {
|
|
1934
|
-
return null;
|
|
1935
|
-
}
|
|
1936
|
-
const now = /* @__PURE__ */ new Date();
|
|
1937
|
-
const rows = await db.select().from(sessions).where(and(eq(sessions.id, sessionId)));
|
|
1938
|
-
const row = rows[0];
|
|
1939
|
-
if (!row) return null;
|
|
1940
|
-
if (row.expiresAt <= now) {
|
|
1941
|
-
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
|
1942
|
-
return null;
|
|
1943
|
-
}
|
|
1944
|
-
return rowToSession2(row);
|
|
1945
|
-
}
|
|
1946
|
-
async function revoke(sessionId) {
|
|
1947
|
-
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
|
1948
|
-
}
|
|
1949
|
-
async function revokeAll(userId) {
|
|
1950
|
-
await db.delete(sessions).where(eq(sessions.userId, userId));
|
|
1951
|
-
}
|
|
1952
|
-
async function list(userId) {
|
|
1953
|
-
const now = /* @__PURE__ */ new Date();
|
|
1954
|
-
const rows = await db.select().from(sessions).where(and(eq(sessions.userId, userId)));
|
|
1955
|
-
return rows.filter((row) => row.expiresAt > now).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).map(rowToSession2);
|
|
1956
|
-
}
|
|
1957
|
-
return { create, validate, revoke, revokeAll, list };
|
|
1958
|
-
}
|
|
1959
1890
|
|
|
1960
|
-
// src/
|
|
1961
|
-
function
|
|
1891
|
+
// src/plugin/helpers.ts
|
|
1892
|
+
function json(body, status = 200) {
|
|
1962
1893
|
return new Response(JSON.stringify(body), {
|
|
1963
1894
|
status,
|
|
1964
1895
|
headers: { "Content-Type": "application/json" }
|
|
@@ -1966,18 +1897,41 @@ function jsonResponse2(body, status = 200) {
|
|
|
1966
1897
|
}
|
|
1967
1898
|
async function parseBody(request) {
|
|
1968
1899
|
try {
|
|
1969
|
-
|
|
1900
|
+
const data = await request.json();
|
|
1901
|
+
return { ok: true, data };
|
|
1970
1902
|
} catch {
|
|
1971
|
-
return {
|
|
1903
|
+
return {
|
|
1904
|
+
ok: false,
|
|
1905
|
+
response: json({ error: "Invalid JSON body" }, 400)
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function getCookie(request, name) {
|
|
1910
|
+
const header = request.headers.get("cookie");
|
|
1911
|
+
if (!header) return null;
|
|
1912
|
+
const match = header.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
|
|
1913
|
+
return match?.[1] ? decodeURIComponent(match[1]) : null;
|
|
1914
|
+
}
|
|
1915
|
+
function buildSetCookie(name, value, maxAge, path = "/") {
|
|
1916
|
+
return `${name}=${encodeURIComponent(value)}; HttpOnly; Secure; SameSite=Lax; Path=${path}; Max-Age=${maxAge}`;
|
|
1917
|
+
}
|
|
1918
|
+
function buildClearCookie(name, path = "/") {
|
|
1919
|
+
return `${name}=; HttpOnly; Secure; SameSite=Lax; Path=${path}; Max-Age=0`;
|
|
1920
|
+
}
|
|
1921
|
+
function extractToken(request, cookieName = "kavach_session") {
|
|
1922
|
+
const authHeader = request.headers.get("authorization");
|
|
1923
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
1924
|
+
return authHeader.slice(7);
|
|
1972
1925
|
}
|
|
1926
|
+
return getCookie(request, cookieName);
|
|
1973
1927
|
}
|
|
1928
|
+
|
|
1929
|
+
// src/auth/admin-plugin.ts
|
|
1974
1930
|
function admin(config) {
|
|
1975
1931
|
return {
|
|
1976
1932
|
id: "kavach-admin",
|
|
1977
1933
|
async init(ctx) {
|
|
1978
|
-
const
|
|
1979
|
-
const sessionManager = sessionConfig ? createSessionManager(sessionConfig, ctx.db) : null;
|
|
1980
|
-
const module = createAdminModule(config ?? {}, ctx.db, sessionManager);
|
|
1934
|
+
const module = createAdminModule(config ?? {}, ctx.db, ctx.sessionManager ?? null);
|
|
1981
1935
|
ctx.addEndpoint({
|
|
1982
1936
|
method: "GET",
|
|
1983
1937
|
path: "/auth/admin/users",
|
|
@@ -1988,11 +1942,11 @@ function admin(config) {
|
|
|
1988
1942
|
async handler(request, endpointCtx) {
|
|
1989
1943
|
const user = await endpointCtx.getUser(request);
|
|
1990
1944
|
if (!user) {
|
|
1991
|
-
return
|
|
1945
|
+
return json({ error: "Authentication required" }, 401);
|
|
1992
1946
|
}
|
|
1993
1947
|
const isAdminUser = await module.isAdmin(user.id);
|
|
1994
1948
|
if (!isAdminUser) {
|
|
1995
|
-
return
|
|
1949
|
+
return json({ error: "Admin access required" }, 403);
|
|
1996
1950
|
}
|
|
1997
1951
|
const url = new URL(request.url);
|
|
1998
1952
|
const limitParam = url.searchParams.get("limit");
|
|
@@ -2003,7 +1957,7 @@ function admin(config) {
|
|
|
2003
1957
|
offset: offsetParam ? Number(offsetParam) : void 0,
|
|
2004
1958
|
search
|
|
2005
1959
|
});
|
|
2006
|
-
return
|
|
1960
|
+
return json(result);
|
|
2007
1961
|
}
|
|
2008
1962
|
});
|
|
2009
1963
|
ctx.addEndpoint({
|
|
@@ -2016,23 +1970,23 @@ function admin(config) {
|
|
|
2016
1970
|
async handler(request, endpointCtx) {
|
|
2017
1971
|
const user = await endpointCtx.getUser(request);
|
|
2018
1972
|
if (!user) {
|
|
2019
|
-
return
|
|
1973
|
+
return json({ error: "Authentication required" }, 401);
|
|
2020
1974
|
}
|
|
2021
1975
|
const isAdminUser = await module.isAdmin(user.id);
|
|
2022
1976
|
if (!isAdminUser) {
|
|
2023
|
-
return
|
|
1977
|
+
return json({ error: "Admin access required" }, 403);
|
|
2024
1978
|
}
|
|
2025
1979
|
const url = new URL(request.url);
|
|
2026
1980
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2027
1981
|
const targetId = segments[3];
|
|
2028
1982
|
if (!targetId) {
|
|
2029
|
-
return
|
|
1983
|
+
return json({ error: "Missing user ID in path" }, 400);
|
|
2030
1984
|
}
|
|
2031
1985
|
const found = await module.getUser(decodeURIComponent(targetId));
|
|
2032
1986
|
if (!found) {
|
|
2033
|
-
return
|
|
1987
|
+
return json({ error: "User not found" }, 404);
|
|
2034
1988
|
}
|
|
2035
|
-
return
|
|
1989
|
+
return json(found);
|
|
2036
1990
|
}
|
|
2037
1991
|
});
|
|
2038
1992
|
ctx.addEndpoint({
|
|
@@ -2045,23 +1999,24 @@ function admin(config) {
|
|
|
2045
1999
|
async handler(request, endpointCtx) {
|
|
2046
2000
|
const user = await endpointCtx.getUser(request);
|
|
2047
2001
|
if (!user) {
|
|
2048
|
-
return
|
|
2002
|
+
return json({ error: "Authentication required" }, 401);
|
|
2049
2003
|
}
|
|
2050
2004
|
const isAdminUser = await module.isAdmin(user.id);
|
|
2051
2005
|
if (!isAdminUser) {
|
|
2052
|
-
return
|
|
2006
|
+
return json({ error: "Admin access required" }, 403);
|
|
2053
2007
|
}
|
|
2054
2008
|
const url = new URL(request.url);
|
|
2055
2009
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2056
2010
|
const targetId = segments[3];
|
|
2057
2011
|
if (!targetId) {
|
|
2058
|
-
return
|
|
2012
|
+
return json({ error: "Missing user ID in path" }, 400);
|
|
2059
2013
|
}
|
|
2060
|
-
const
|
|
2061
|
-
|
|
2062
|
-
const
|
|
2014
|
+
const bodyResult = await parseBody(request);
|
|
2015
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
2016
|
+
const reason = typeof bodyResult.data.reason === "string" ? bodyResult.data.reason : void 0;
|
|
2017
|
+
const expiresAt = bodyResult.data.expiresAt ? new Date(bodyResult.data.expiresAt) : void 0;
|
|
2063
2018
|
await module.banUser(decodeURIComponent(targetId), reason, expiresAt);
|
|
2064
|
-
return
|
|
2019
|
+
return json({ success: true });
|
|
2065
2020
|
}
|
|
2066
2021
|
});
|
|
2067
2022
|
ctx.addEndpoint({
|
|
@@ -2074,20 +2029,20 @@ function admin(config) {
|
|
|
2074
2029
|
async handler(request, endpointCtx) {
|
|
2075
2030
|
const user = await endpointCtx.getUser(request);
|
|
2076
2031
|
if (!user) {
|
|
2077
|
-
return
|
|
2032
|
+
return json({ error: "Authentication required" }, 401);
|
|
2078
2033
|
}
|
|
2079
2034
|
const isAdminUser = await module.isAdmin(user.id);
|
|
2080
2035
|
if (!isAdminUser) {
|
|
2081
|
-
return
|
|
2036
|
+
return json({ error: "Admin access required" }, 403);
|
|
2082
2037
|
}
|
|
2083
2038
|
const url = new URL(request.url);
|
|
2084
2039
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2085
2040
|
const targetId = segments[3];
|
|
2086
2041
|
if (!targetId) {
|
|
2087
|
-
return
|
|
2042
|
+
return json({ error: "Missing user ID in path" }, 400);
|
|
2088
2043
|
}
|
|
2089
2044
|
await module.unbanUser(decodeURIComponent(targetId));
|
|
2090
|
-
return
|
|
2045
|
+
return json({ success: true });
|
|
2091
2046
|
}
|
|
2092
2047
|
});
|
|
2093
2048
|
ctx.addEndpoint({
|
|
@@ -2100,20 +2055,20 @@ function admin(config) {
|
|
|
2100
2055
|
async handler(request, endpointCtx) {
|
|
2101
2056
|
const user = await endpointCtx.getUser(request);
|
|
2102
2057
|
if (!user) {
|
|
2103
|
-
return
|
|
2058
|
+
return json({ error: "Authentication required" }, 401);
|
|
2104
2059
|
}
|
|
2105
2060
|
const isAdminUser = await module.isAdmin(user.id);
|
|
2106
2061
|
if (!isAdminUser) {
|
|
2107
|
-
return
|
|
2062
|
+
return json({ error: "Admin access required" }, 403);
|
|
2108
2063
|
}
|
|
2109
2064
|
const url = new URL(request.url);
|
|
2110
2065
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2111
2066
|
const targetId = segments[3];
|
|
2112
2067
|
if (!targetId) {
|
|
2113
|
-
return
|
|
2068
|
+
return json({ error: "Missing user ID in path" }, 400);
|
|
2114
2069
|
}
|
|
2115
2070
|
await module.deleteUser(decodeURIComponent(targetId));
|
|
2116
|
-
return
|
|
2071
|
+
return json({ success: true });
|
|
2117
2072
|
}
|
|
2118
2073
|
});
|
|
2119
2074
|
ctx.addEndpoint({
|
|
@@ -2126,23 +2081,23 @@ function admin(config) {
|
|
|
2126
2081
|
async handler(request, endpointCtx) {
|
|
2127
2082
|
const user = await endpointCtx.getUser(request);
|
|
2128
2083
|
if (!user) {
|
|
2129
|
-
return
|
|
2084
|
+
return json({ error: "Authentication required" }, 401);
|
|
2130
2085
|
}
|
|
2131
2086
|
const isAdminUser = await module.isAdmin(user.id);
|
|
2132
2087
|
if (!isAdminUser) {
|
|
2133
|
-
return
|
|
2088
|
+
return json({ error: "Admin access required" }, 403);
|
|
2134
2089
|
}
|
|
2135
2090
|
const url = new URL(request.url);
|
|
2136
2091
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2137
2092
|
const targetId = segments[3];
|
|
2138
2093
|
if (!targetId) {
|
|
2139
|
-
return
|
|
2094
|
+
return json({ error: "Missing user ID in path" }, 400);
|
|
2140
2095
|
}
|
|
2141
2096
|
try {
|
|
2142
2097
|
const result = await module.impersonate(user.id, decodeURIComponent(targetId));
|
|
2143
|
-
return
|
|
2098
|
+
return json(result);
|
|
2144
2099
|
} catch (err2) {
|
|
2145
|
-
return
|
|
2100
|
+
return json(
|
|
2146
2101
|
{ error: err2 instanceof Error ? err2.message : "Impersonation failed" },
|
|
2147
2102
|
403
|
|
2148
2103
|
);
|
|
@@ -2221,29 +2176,14 @@ function createAnonymousAuthModule(config, db, sessionManager) {
|
|
|
2221
2176
|
}
|
|
2222
2177
|
|
|
2223
2178
|
// src/auth/anonymous-plugin.ts
|
|
2224
|
-
function jsonResponse3(body, status = 200) {
|
|
2225
|
-
return new Response(JSON.stringify(body), {
|
|
2226
|
-
status,
|
|
2227
|
-
headers: { "Content-Type": "application/json" }
|
|
2228
|
-
});
|
|
2229
|
-
}
|
|
2230
|
-
async function parseBody2(request) {
|
|
2231
|
-
try {
|
|
2232
|
-
return await request.json();
|
|
2233
|
-
} catch {
|
|
2234
|
-
return {};
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
2179
|
function anonymousAuth(config) {
|
|
2238
2180
|
return {
|
|
2239
2181
|
id: "kavach-anonymous",
|
|
2240
2182
|
async init(ctx) {
|
|
2241
|
-
|
|
2242
|
-
if (!sessionSecret) {
|
|
2183
|
+
if (!ctx.sessionManager) {
|
|
2243
2184
|
throw new Error("anonymousAuth plugin requires auth.session.secret to be configured");
|
|
2244
2185
|
}
|
|
2245
|
-
const
|
|
2246
|
-
const mod = createAnonymousAuthModule(config ?? {}, ctx.db, sessionManager);
|
|
2186
|
+
const mod = createAnonymousAuthModule(config ?? {}, ctx.db, ctx.sessionManager);
|
|
2247
2187
|
ctx.addEndpoint({
|
|
2248
2188
|
method: "POST",
|
|
2249
2189
|
path: "/auth/anonymous",
|
|
@@ -2254,9 +2194,9 @@ function anonymousAuth(config) {
|
|
|
2254
2194
|
async handler(_request, _endpointCtx) {
|
|
2255
2195
|
try {
|
|
2256
2196
|
const result = await mod.createAnonymousUser();
|
|
2257
|
-
return
|
|
2197
|
+
return json({ userId: result.userId, sessionToken: result.sessionToken });
|
|
2258
2198
|
} catch (err2) {
|
|
2259
|
-
return
|
|
2199
|
+
return json(
|
|
2260
2200
|
{ error: err2 instanceof Error ? err2.message : "Failed to create anonymous user" },
|
|
2261
2201
|
500
|
|
2262
2202
|
);
|
|
@@ -2273,21 +2213,22 @@ function anonymousAuth(config) {
|
|
|
2273
2213
|
async handler(request, endpointCtx) {
|
|
2274
2214
|
const user = await endpointCtx.getUser(request);
|
|
2275
2215
|
if (!user) {
|
|
2276
|
-
return
|
|
2216
|
+
return json({ error: "Authentication required" }, 401);
|
|
2277
2217
|
}
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2280
|
-
const
|
|
2218
|
+
const bodyResult = await parseBody(request);
|
|
2219
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
2220
|
+
const email = typeof bodyResult.data.email === "string" ? bodyResult.data.email.trim() : null;
|
|
2221
|
+
const name = typeof bodyResult.data.name === "string" ? bodyResult.data.name.trim() : void 0;
|
|
2281
2222
|
if (!email) {
|
|
2282
|
-
return
|
|
2223
|
+
return json({ error: "Missing required field: email" }, 400);
|
|
2283
2224
|
}
|
|
2284
2225
|
try {
|
|
2285
2226
|
await mod.upgradeUser(user.id, { email, name });
|
|
2286
|
-
return
|
|
2227
|
+
return json({ upgraded: true });
|
|
2287
2228
|
} catch (err2) {
|
|
2288
2229
|
const message = err2 instanceof Error ? err2.message : "Upgrade failed";
|
|
2289
2230
|
const status = message.includes("not an anonymous user") ? 400 : 500;
|
|
2290
|
-
return
|
|
2231
|
+
return json({ error: message }, status);
|
|
2291
2232
|
}
|
|
2292
2233
|
}
|
|
2293
2234
|
});
|
|
@@ -2301,10 +2242,10 @@ function anonymousAuth(config) {
|
|
|
2301
2242
|
async handler(request, endpointCtx) {
|
|
2302
2243
|
const user = await endpointCtx.getUser(request);
|
|
2303
2244
|
if (!user) {
|
|
2304
|
-
return
|
|
2245
|
+
return json({ error: "Authentication required" }, 401);
|
|
2305
2246
|
}
|
|
2306
2247
|
const anonymous = await mod.isAnonymous(user.id);
|
|
2307
|
-
return
|
|
2248
|
+
return json({ anonymous });
|
|
2308
2249
|
}
|
|
2309
2250
|
});
|
|
2310
2251
|
}
|
|
@@ -2410,7 +2351,7 @@ function createApiKeyManagerModule(config, db) {
|
|
|
2410
2351
|
const url = new URL(request.url);
|
|
2411
2352
|
const { pathname } = url;
|
|
2412
2353
|
const { method } = request;
|
|
2413
|
-
const
|
|
2354
|
+
const json2 = (data, status = 200) => new Response(JSON.stringify(data), {
|
|
2414
2355
|
status,
|
|
2415
2356
|
headers: { "Content-Type": "application/json" }
|
|
2416
2357
|
});
|
|
@@ -2419,11 +2360,11 @@ function createApiKeyManagerModule(config, db) {
|
|
|
2419
2360
|
try {
|
|
2420
2361
|
body = await request.json();
|
|
2421
2362
|
} catch {
|
|
2422
|
-
return
|
|
2363
|
+
return json2({ error: "Invalid JSON body" }, 400);
|
|
2423
2364
|
}
|
|
2424
2365
|
const b = body;
|
|
2425
2366
|
if (typeof b.userId !== "string" || typeof b.name !== "string" || !Array.isArray(b.permissions)) {
|
|
2426
|
-
return
|
|
2367
|
+
return json2({ error: "Missing required fields: userId, name, permissions" }, 400);
|
|
2427
2368
|
}
|
|
2428
2369
|
const expiresAt = b.expiresAt ? new Date(b.expiresAt) : void 0;
|
|
2429
2370
|
const result = await create({
|
|
@@ -2432,28 +2373,28 @@ function createApiKeyManagerModule(config, db) {
|
|
|
2432
2373
|
permissions: b.permissions,
|
|
2433
2374
|
expiresAt
|
|
2434
2375
|
});
|
|
2435
|
-
return
|
|
2376
|
+
return json2(result, 201);
|
|
2436
2377
|
}
|
|
2437
2378
|
const listMatch = /^\/auth\/api-keys\/([^/]+)$/.exec(pathname);
|
|
2438
2379
|
if (method === "GET" && listMatch) {
|
|
2439
2380
|
const userId = decodeURIComponent(listMatch[1] ?? "");
|
|
2440
2381
|
const keys = await list(userId);
|
|
2441
|
-
return
|
|
2382
|
+
return json2(keys);
|
|
2442
2383
|
}
|
|
2443
2384
|
const deleteMatch = /^\/auth\/api-keys\/([^/]+)$/.exec(pathname);
|
|
2444
2385
|
if (method === "DELETE" && deleteMatch) {
|
|
2445
2386
|
const keyId = decodeURIComponent(deleteMatch[1] ?? "");
|
|
2446
2387
|
await revoke(keyId);
|
|
2447
|
-
return
|
|
2388
|
+
return json2({ success: true });
|
|
2448
2389
|
}
|
|
2449
2390
|
const rotateMatch = /^\/auth\/api-keys\/([^/]+)\/rotate$/.exec(pathname);
|
|
2450
2391
|
if (method === "POST" && rotateMatch) {
|
|
2451
2392
|
const keyId = decodeURIComponent(rotateMatch[1] ?? "");
|
|
2452
2393
|
try {
|
|
2453
2394
|
const result = await rotate(keyId);
|
|
2454
|
-
return
|
|
2395
|
+
return json2(result);
|
|
2455
2396
|
} catch (err2) {
|
|
2456
|
-
return
|
|
2397
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 404);
|
|
2457
2398
|
}
|
|
2458
2399
|
}
|
|
2459
2400
|
return null;
|
|
@@ -2470,19 +2411,6 @@ function createApiKeyManagerModule(config, db) {
|
|
|
2470
2411
|
}
|
|
2471
2412
|
|
|
2472
2413
|
// src/auth/api-key-plugin.ts
|
|
2473
|
-
function jsonResponse4(body, status = 200) {
|
|
2474
|
-
return new Response(JSON.stringify(body), {
|
|
2475
|
-
status,
|
|
2476
|
-
headers: { "Content-Type": "application/json" }
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
async function parseBody3(request) {
|
|
2480
|
-
try {
|
|
2481
|
-
return await request.json();
|
|
2482
|
-
} catch {
|
|
2483
|
-
return {};
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
2414
|
function apiKeys2(config) {
|
|
2487
2415
|
return {
|
|
2488
2416
|
id: "kavach-api-key",
|
|
@@ -2498,15 +2426,16 @@ function apiKeys2(config) {
|
|
|
2498
2426
|
async handler(request, endpointCtx) {
|
|
2499
2427
|
const user = await endpointCtx.getUser(request);
|
|
2500
2428
|
if (!user) {
|
|
2501
|
-
return
|
|
2429
|
+
return json({ error: "Authentication required" }, 401);
|
|
2502
2430
|
}
|
|
2503
|
-
const
|
|
2504
|
-
|
|
2505
|
-
const
|
|
2431
|
+
const bodyResult = await parseBody(request);
|
|
2432
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
2433
|
+
const name = typeof bodyResult.data.name === "string" ? bodyResult.data.name.trim() : null;
|
|
2434
|
+
const permissions2 = Array.isArray(bodyResult.data.permissions) ? bodyResult.data.permissions : null;
|
|
2506
2435
|
if (!name || !permissions2) {
|
|
2507
|
-
return
|
|
2436
|
+
return json({ error: "Missing required fields: name, permissions" }, 400);
|
|
2508
2437
|
}
|
|
2509
|
-
const expiresAt =
|
|
2438
|
+
const expiresAt = bodyResult.data.expiresAt ? new Date(bodyResult.data.expiresAt) : void 0;
|
|
2510
2439
|
try {
|
|
2511
2440
|
const result = await module.create({
|
|
2512
2441
|
userId: user.id,
|
|
@@ -2514,9 +2443,9 @@ function apiKeys2(config) {
|
|
|
2514
2443
|
permissions: permissions2,
|
|
2515
2444
|
expiresAt
|
|
2516
2445
|
});
|
|
2517
|
-
return
|
|
2446
|
+
return json(result, 201);
|
|
2518
2447
|
} catch (err2) {
|
|
2519
|
-
return
|
|
2448
|
+
return json(
|
|
2520
2449
|
{ error: err2 instanceof Error ? err2.message : "Failed to create API key" },
|
|
2521
2450
|
500
|
|
2522
2451
|
);
|
|
@@ -2533,10 +2462,10 @@ function apiKeys2(config) {
|
|
|
2533
2462
|
async handler(request, endpointCtx) {
|
|
2534
2463
|
const user = await endpointCtx.getUser(request);
|
|
2535
2464
|
if (!user) {
|
|
2536
|
-
return
|
|
2465
|
+
return json({ error: "Authentication required" }, 401);
|
|
2537
2466
|
}
|
|
2538
2467
|
const keys = await module.list(user.id);
|
|
2539
|
-
return
|
|
2468
|
+
return json({ apiKeys: keys });
|
|
2540
2469
|
}
|
|
2541
2470
|
});
|
|
2542
2471
|
ctx.addEndpoint({
|
|
@@ -2549,21 +2478,21 @@ function apiKeys2(config) {
|
|
|
2549
2478
|
async handler(request, endpointCtx) {
|
|
2550
2479
|
const user = await endpointCtx.getUser(request);
|
|
2551
2480
|
if (!user) {
|
|
2552
|
-
return
|
|
2481
|
+
return json({ error: "Authentication required" }, 401);
|
|
2553
2482
|
}
|
|
2554
2483
|
const url = new URL(request.url);
|
|
2555
2484
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2556
2485
|
const keyId = segments[2];
|
|
2557
2486
|
if (!keyId) {
|
|
2558
|
-
return
|
|
2487
|
+
return json({ error: "Missing API key ID in path" }, 400);
|
|
2559
2488
|
}
|
|
2560
2489
|
const keys = await module.list(user.id);
|
|
2561
2490
|
const owned = keys.some((k) => k.id === decodeURIComponent(keyId));
|
|
2562
2491
|
if (!owned) {
|
|
2563
|
-
return
|
|
2492
|
+
return json({ error: "API key not found" }, 404);
|
|
2564
2493
|
}
|
|
2565
2494
|
await module.revoke(decodeURIComponent(keyId));
|
|
2566
|
-
return
|
|
2495
|
+
return json({ revoked: true });
|
|
2567
2496
|
}
|
|
2568
2497
|
});
|
|
2569
2498
|
ctx.addEndpoint({
|
|
@@ -2576,24 +2505,24 @@ function apiKeys2(config) {
|
|
|
2576
2505
|
async handler(request, endpointCtx) {
|
|
2577
2506
|
const user = await endpointCtx.getUser(request);
|
|
2578
2507
|
if (!user) {
|
|
2579
|
-
return
|
|
2508
|
+
return json({ error: "Authentication required" }, 401);
|
|
2580
2509
|
}
|
|
2581
2510
|
const url = new URL(request.url);
|
|
2582
2511
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
2583
2512
|
const keyId = segments[2];
|
|
2584
2513
|
if (!keyId) {
|
|
2585
|
-
return
|
|
2514
|
+
return json({ error: "Missing API key ID in path" }, 400);
|
|
2586
2515
|
}
|
|
2587
2516
|
const keys = await module.list(user.id);
|
|
2588
2517
|
const owned = keys.some((k) => k.id === decodeURIComponent(keyId));
|
|
2589
2518
|
if (!owned) {
|
|
2590
|
-
return
|
|
2519
|
+
return json({ error: "API key not found" }, 404);
|
|
2591
2520
|
}
|
|
2592
2521
|
try {
|
|
2593
2522
|
const result = await module.rotate(decodeURIComponent(keyId));
|
|
2594
|
-
return
|
|
2523
|
+
return json(result);
|
|
2595
2524
|
} catch (err2) {
|
|
2596
|
-
return
|
|
2525
|
+
return json(
|
|
2597
2526
|
{ error: err2 instanceof Error ? err2.message : "Failed to rotate API key" },
|
|
2598
2527
|
400
|
|
2599
2528
|
);
|
|
@@ -2983,13 +2912,13 @@ function customSession(config = {}) {
|
|
|
2983
2912
|
const url = new URL(request.url);
|
|
2984
2913
|
const sessionId = url.searchParams.get("sessionId");
|
|
2985
2914
|
if (!sessionId) {
|
|
2986
|
-
return
|
|
2915
|
+
return jsonResponse2({ error: "Missing required query parameter: sessionId" }, 400);
|
|
2987
2916
|
}
|
|
2988
2917
|
try {
|
|
2989
2918
|
const fields = await mod.getSessionFields(sessionId);
|
|
2990
|
-
return
|
|
2919
|
+
return jsonResponse2({ fields: fields ?? {} });
|
|
2991
2920
|
} catch (err2) {
|
|
2992
|
-
return
|
|
2921
|
+
return jsonResponse2(
|
|
2993
2922
|
{ error: err2 instanceof Error ? err2.message : "Failed to get session fields" },
|
|
2994
2923
|
500
|
|
2995
2924
|
);
|
|
@@ -3008,23 +2937,23 @@ function customSession(config = {}) {
|
|
|
3008
2937
|
try {
|
|
3009
2938
|
body = await request.json();
|
|
3010
2939
|
} catch {
|
|
3011
|
-
return
|
|
2940
|
+
return jsonResponse2({ error: "Invalid JSON body" }, 400);
|
|
3012
2941
|
}
|
|
3013
2942
|
const sessionId = typeof body.sessionId === "string" ? body.sessionId : null;
|
|
3014
2943
|
if (!sessionId) {
|
|
3015
|
-
return
|
|
2944
|
+
return jsonResponse2({ error: "Missing required field: sessionId" }, 400);
|
|
3016
2945
|
}
|
|
3017
2946
|
const fields = body.fields !== null && body.fields !== void 0 && typeof body.fields === "object" && !Array.isArray(body.fields) ? body.fields : null;
|
|
3018
2947
|
if (!fields) {
|
|
3019
|
-
return
|
|
2948
|
+
return jsonResponse2({ error: "Missing or invalid field: fields" }, 400);
|
|
3020
2949
|
}
|
|
3021
2950
|
try {
|
|
3022
2951
|
await mod.updateSessionFields(sessionId, fields);
|
|
3023
|
-
return
|
|
2952
|
+
return jsonResponse2({ updated: true });
|
|
3024
2953
|
} catch (err2) {
|
|
3025
2954
|
const message = err2 instanceof Error ? err2.message : "Update failed";
|
|
3026
2955
|
const status = message.includes("not found") ? 404 : 500;
|
|
3027
|
-
return
|
|
2956
|
+
return jsonResponse2({ error: message }, status);
|
|
3028
2957
|
}
|
|
3029
2958
|
}
|
|
3030
2959
|
});
|
|
@@ -3034,7 +2963,7 @@ function customSession(config = {}) {
|
|
|
3034
2963
|
}
|
|
3035
2964
|
};
|
|
3036
2965
|
}
|
|
3037
|
-
function
|
|
2966
|
+
function jsonResponse2(body, status = 200) {
|
|
3038
2967
|
return new Response(JSON.stringify(body), {
|
|
3039
2968
|
status,
|
|
3040
2969
|
headers: { "Content-Type": "application/json" }
|
|
@@ -3047,13 +2976,13 @@ var DEFAULT_CODE_EXPIRY_SECONDS = 900;
|
|
|
3047
2976
|
var DEFAULT_POLL_INTERVAL_SECONDS = 5;
|
|
3048
2977
|
var USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ";
|
|
3049
2978
|
var SLOW_DOWN_THRESHOLD_MS = 4e3;
|
|
3050
|
-
function
|
|
2979
|
+
function jsonResponse3(body, status = 200) {
|
|
3051
2980
|
return new Response(JSON.stringify(body), {
|
|
3052
2981
|
status,
|
|
3053
2982
|
headers: { "Content-Type": "application/json" }
|
|
3054
2983
|
});
|
|
3055
2984
|
}
|
|
3056
|
-
async function
|
|
2985
|
+
async function parseBody2(request) {
|
|
3057
2986
|
try {
|
|
3058
2987
|
return await request.json();
|
|
3059
2988
|
} catch {
|
|
@@ -3177,7 +3106,7 @@ function createDeviceAuthModule(config) {
|
|
|
3177
3106
|
const { method, pathname } = { method: request.method, pathname: url.pathname };
|
|
3178
3107
|
if (method === "POST" && pathname.endsWith("/auth/device/code")) {
|
|
3179
3108
|
const response = await requestCode();
|
|
3180
|
-
return
|
|
3109
|
+
return jsonResponse3({
|
|
3181
3110
|
device_code: response.deviceCode,
|
|
3182
3111
|
user_code: response.userCode,
|
|
3183
3112
|
verification_uri: response.verificationUri,
|
|
@@ -3187,17 +3116,17 @@ function createDeviceAuthModule(config) {
|
|
|
3187
3116
|
});
|
|
3188
3117
|
}
|
|
3189
3118
|
if (method === "POST" && pathname.endsWith("/auth/device/token")) {
|
|
3190
|
-
const body = await
|
|
3119
|
+
const body = await parseBody2(request);
|
|
3191
3120
|
const deviceCode = typeof body.device_code === "string" ? body.device_code : null;
|
|
3192
3121
|
if (!deviceCode) {
|
|
3193
|
-
return
|
|
3122
|
+
return jsonResponse3(
|
|
3194
3123
|
{ error: "invalid_request", error_description: "Missing device_code" },
|
|
3195
3124
|
400
|
|
3196
3125
|
);
|
|
3197
3126
|
}
|
|
3198
3127
|
const grant = grantsByDevice.get(deviceCode);
|
|
3199
3128
|
if (grant?.lastPolledAt && Date.now() - grant.lastPolledAt < SLOW_DOWN_THRESHOLD_MS) {
|
|
3200
|
-
return
|
|
3129
|
+
return jsonResponse3(
|
|
3201
3130
|
{
|
|
3202
3131
|
error: "slow_down",
|
|
3203
3132
|
error_description: "Polling too frequently",
|
|
@@ -3208,10 +3137,10 @@ function createDeviceAuthModule(config) {
|
|
|
3208
3137
|
}
|
|
3209
3138
|
const status = await checkAuthorization(deviceCode);
|
|
3210
3139
|
if (status.status === "authorized") {
|
|
3211
|
-
return
|
|
3140
|
+
return jsonResponse3({ authorized: true, user_id: status.userId });
|
|
3212
3141
|
}
|
|
3213
3142
|
if (status.status === "pending") {
|
|
3214
|
-
return
|
|
3143
|
+
return jsonResponse3(
|
|
3215
3144
|
{
|
|
3216
3145
|
error: "authorization_pending",
|
|
3217
3146
|
error_description: "The user has not yet authorized the device"
|
|
@@ -3220,7 +3149,7 @@ function createDeviceAuthModule(config) {
|
|
|
3220
3149
|
);
|
|
3221
3150
|
}
|
|
3222
3151
|
if (status.status === "denied") {
|
|
3223
|
-
return
|
|
3152
|
+
return jsonResponse3(
|
|
3224
3153
|
{
|
|
3225
3154
|
error: "access_denied",
|
|
3226
3155
|
error_description: "The user denied the authorization request"
|
|
@@ -3228,7 +3157,7 @@ function createDeviceAuthModule(config) {
|
|
|
3228
3157
|
400
|
|
3229
3158
|
);
|
|
3230
3159
|
}
|
|
3231
|
-
return
|
|
3160
|
+
return jsonResponse3(
|
|
3232
3161
|
{
|
|
3233
3162
|
error: "expired_token",
|
|
3234
3163
|
error_description: "The device code has expired"
|
|
@@ -3237,12 +3166,12 @@ function createDeviceAuthModule(config) {
|
|
|
3237
3166
|
);
|
|
3238
3167
|
}
|
|
3239
3168
|
if (method === "POST" && pathname.endsWith("/auth/device/authorize")) {
|
|
3240
|
-
const body = await
|
|
3169
|
+
const body = await parseBody2(request);
|
|
3241
3170
|
const userCode = typeof body.user_code === "string" ? body.user_code : null;
|
|
3242
3171
|
const userId = typeof body.user_id === "string" ? body.user_id : null;
|
|
3243
3172
|
const action = typeof body.action === "string" ? body.action : "approve";
|
|
3244
3173
|
if (!userCode || !userId) {
|
|
3245
|
-
return
|
|
3174
|
+
return jsonResponse3(
|
|
3246
3175
|
{ error: "invalid_request", error_description: "Missing user_code or user_id" },
|
|
3247
3176
|
400
|
|
3248
3177
|
);
|
|
@@ -3250,12 +3179,12 @@ function createDeviceAuthModule(config) {
|
|
|
3250
3179
|
try {
|
|
3251
3180
|
if (action === "deny") {
|
|
3252
3181
|
await deny(userCode);
|
|
3253
|
-
return
|
|
3182
|
+
return jsonResponse3({ denied: true });
|
|
3254
3183
|
}
|
|
3255
3184
|
await authorize(userCode, userId);
|
|
3256
|
-
return
|
|
3185
|
+
return jsonResponse3({ authorized: true });
|
|
3257
3186
|
} catch (err2) {
|
|
3258
|
-
return
|
|
3187
|
+
return jsonResponse3(
|
|
3259
3188
|
{
|
|
3260
3189
|
error: "invalid_request",
|
|
3261
3190
|
error_description: err2 instanceof Error ? err2.message : "Authorization failed"
|
|
@@ -3536,31 +3465,16 @@ function createEmailOtpModule(config, db, sessionManager) {
|
|
|
3536
3465
|
}
|
|
3537
3466
|
|
|
3538
3467
|
// src/auth/email-otp-plugin.ts
|
|
3539
|
-
function jsonResponse7(body, status = 200) {
|
|
3540
|
-
return new Response(JSON.stringify(body), {
|
|
3541
|
-
status,
|
|
3542
|
-
headers: { "Content-Type": "application/json" }
|
|
3543
|
-
});
|
|
3544
|
-
}
|
|
3545
|
-
async function parseBody5(request) {
|
|
3546
|
-
try {
|
|
3547
|
-
return await request.json();
|
|
3548
|
-
} catch {
|
|
3549
|
-
return {};
|
|
3550
|
-
}
|
|
3551
|
-
}
|
|
3552
3468
|
function emailOtp(config) {
|
|
3553
3469
|
return {
|
|
3554
3470
|
id: "kavach-email-otp",
|
|
3555
3471
|
async init(ctx) {
|
|
3556
|
-
|
|
3557
|
-
if (!sessionConfig) {
|
|
3472
|
+
if (!ctx.sessionManager) {
|
|
3558
3473
|
throw new Error(
|
|
3559
3474
|
"kavach-email-otp plugin requires auth.session to be configured so that sessions can be issued on successful verification."
|
|
3560
3475
|
);
|
|
3561
3476
|
}
|
|
3562
|
-
const
|
|
3563
|
-
const module = createEmailOtpModule(config, ctx.db, sessionManager);
|
|
3477
|
+
const module = createEmailOtpModule(config, ctx.db, ctx.sessionManager);
|
|
3564
3478
|
ctx.addEndpoint({
|
|
3565
3479
|
method: "POST",
|
|
3566
3480
|
path: "/auth/email-otp/send",
|
|
@@ -3569,19 +3483,17 @@ function emailOtp(config) {
|
|
|
3569
3483
|
description: "Send a one-time passcode to the provided email address"
|
|
3570
3484
|
},
|
|
3571
3485
|
async handler(request) {
|
|
3572
|
-
const
|
|
3573
|
-
|
|
3486
|
+
const bodyResult = await parseBody(request);
|
|
3487
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
3488
|
+
const rawEmail = typeof bodyResult.data.email === "string" ? bodyResult.data.email.trim().toLowerCase() : null;
|
|
3574
3489
|
if (!rawEmail) {
|
|
3575
|
-
return
|
|
3490
|
+
return json({ error: "Missing required field: email" }, 400);
|
|
3576
3491
|
}
|
|
3577
3492
|
try {
|
|
3578
3493
|
const result = await module.sendCode(rawEmail);
|
|
3579
|
-
return
|
|
3494
|
+
return json(result);
|
|
3580
3495
|
} catch (err2) {
|
|
3581
|
-
return
|
|
3582
|
-
{ error: err2 instanceof Error ? err2.message : "Failed to send OTP" },
|
|
3583
|
-
500
|
|
3584
|
-
);
|
|
3496
|
+
return json({ error: err2 instanceof Error ? err2.message : "Failed to send OTP" }, 500);
|
|
3585
3497
|
}
|
|
3586
3498
|
}
|
|
3587
3499
|
});
|
|
@@ -3593,17 +3505,18 @@ function emailOtp(config) {
|
|
|
3593
3505
|
description: "Verify an OTP code and return a session on success"
|
|
3594
3506
|
},
|
|
3595
3507
|
async handler(request) {
|
|
3596
|
-
const
|
|
3597
|
-
|
|
3598
|
-
const
|
|
3508
|
+
const bodyResult = await parseBody(request);
|
|
3509
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
3510
|
+
const rawEmail = typeof bodyResult.data.email === "string" ? bodyResult.data.email.trim().toLowerCase() : null;
|
|
3511
|
+
const code2 = typeof bodyResult.data.code === "string" ? bodyResult.data.code.trim() : null;
|
|
3599
3512
|
if (!rawEmail || !code2) {
|
|
3600
|
-
return
|
|
3513
|
+
return json({ error: "Missing required fields: email, code" }, 400);
|
|
3601
3514
|
}
|
|
3602
3515
|
const result = await module.verifyCode(rawEmail, code2);
|
|
3603
3516
|
if (!result) {
|
|
3604
|
-
return
|
|
3517
|
+
return json({ error: "Invalid or expired OTP code" }, 401);
|
|
3605
3518
|
}
|
|
3606
|
-
return
|
|
3519
|
+
return json(result);
|
|
3607
3520
|
}
|
|
3608
3521
|
});
|
|
3609
3522
|
}
|
|
@@ -3614,7 +3527,7 @@ var TOKEN_PURPOSE = "email-verify";
|
|
|
3614
3527
|
function makeError(code2, message, details) {
|
|
3615
3528
|
return { code: code2, message, ...details !== void 0 ? { details } : {} };
|
|
3616
3529
|
}
|
|
3617
|
-
function
|
|
3530
|
+
function jsonResponse4(body, status = 200) {
|
|
3618
3531
|
return new Response(JSON.stringify(body), {
|
|
3619
3532
|
status,
|
|
3620
3533
|
headers: { "Content-Type": "application/json" }
|
|
@@ -3706,29 +3619,29 @@ function createEmailVerificationModule(config, db, tokenModule) {
|
|
|
3706
3619
|
try {
|
|
3707
3620
|
body = await request.json();
|
|
3708
3621
|
} catch {
|
|
3709
|
-
return
|
|
3622
|
+
return jsonResponse4({ error: "Invalid JSON body" }, 400);
|
|
3710
3623
|
}
|
|
3711
3624
|
const b = body;
|
|
3712
3625
|
if (pathname === "/auth/verify-email/send") {
|
|
3713
3626
|
if (typeof b.userId !== "string" || typeof b.email !== "string") {
|
|
3714
|
-
return
|
|
3627
|
+
return jsonResponse4({ error: "Missing required fields: userId, email" }, 400);
|
|
3715
3628
|
}
|
|
3716
3629
|
const result = await sendVerification(b.userId, b.email);
|
|
3717
3630
|
if (!result.success) {
|
|
3718
3631
|
const status = result.error.code === "USER_NOT_FOUND" ? 404 : 500;
|
|
3719
|
-
return
|
|
3632
|
+
return jsonResponse4({ error: result.error.message }, status);
|
|
3720
3633
|
}
|
|
3721
3634
|
return new Response(null, { status: 204 });
|
|
3722
3635
|
}
|
|
3723
3636
|
if (pathname === "/auth/verify-email/confirm") {
|
|
3724
3637
|
if (typeof b.token !== "string") {
|
|
3725
|
-
return
|
|
3638
|
+
return jsonResponse4({ error: "Missing required field: token" }, 400);
|
|
3726
3639
|
}
|
|
3727
3640
|
const result = await verify(b.token);
|
|
3728
3641
|
if (!result.success) {
|
|
3729
|
-
return
|
|
3642
|
+
return jsonResponse4({ error: result.error.message }, 400);
|
|
3730
3643
|
}
|
|
3731
|
-
return
|
|
3644
|
+
return jsonResponse4({ userId: result.data.userId, email: result.data.email });
|
|
3732
3645
|
}
|
|
3733
3646
|
return null;
|
|
3734
3647
|
}
|
|
@@ -4041,7 +3954,7 @@ data: ${data}
|
|
|
4041
3954
|
|
|
4042
3955
|
`;
|
|
4043
3956
|
}
|
|
4044
|
-
function
|
|
3957
|
+
function extractToken2(request) {
|
|
4045
3958
|
const auth = request.headers.get("authorization");
|
|
4046
3959
|
if (auth?.startsWith("Bearer ")) {
|
|
4047
3960
|
return auth.slice(7).trim() || null;
|
|
@@ -4157,7 +4070,7 @@ function createEventStreamModule(config) {
|
|
|
4157
4070
|
start(controller) {
|
|
4158
4071
|
void (async () => {
|
|
4159
4072
|
if (requireAuth) {
|
|
4160
|
-
const token =
|
|
4073
|
+
const token = extractToken2(request) ?? params.token ?? null;
|
|
4161
4074
|
if (!token) {
|
|
4162
4075
|
controller.enqueue(
|
|
4163
4076
|
new TextEncoder().encode(
|
|
@@ -4762,19 +4675,6 @@ function createGdprModule(db) {
|
|
|
4762
4675
|
}
|
|
4763
4676
|
|
|
4764
4677
|
// src/auth/gdpr-plugin.ts
|
|
4765
|
-
function jsonResponse9(body, status = 200) {
|
|
4766
|
-
return new Response(JSON.stringify(body), {
|
|
4767
|
-
status,
|
|
4768
|
-
headers: { "Content-Type": "application/json" }
|
|
4769
|
-
});
|
|
4770
|
-
}
|
|
4771
|
-
async function parseBody6(request) {
|
|
4772
|
-
try {
|
|
4773
|
-
return await request.json();
|
|
4774
|
-
} catch {
|
|
4775
|
-
return {};
|
|
4776
|
-
}
|
|
4777
|
-
}
|
|
4778
4678
|
function gdpr() {
|
|
4779
4679
|
return {
|
|
4780
4680
|
id: "kavach-gdpr",
|
|
@@ -4790,16 +4690,13 @@ function gdpr() {
|
|
|
4790
4690
|
async handler(request, endpointCtx) {
|
|
4791
4691
|
const user = await endpointCtx.getUser(request);
|
|
4792
4692
|
if (!user) {
|
|
4793
|
-
return
|
|
4693
|
+
return json({ error: "Authentication required" }, 401);
|
|
4794
4694
|
}
|
|
4795
4695
|
try {
|
|
4796
4696
|
const data = await module.exportUserData(user.id);
|
|
4797
|
-
return
|
|
4697
|
+
return json(data);
|
|
4798
4698
|
} catch (err2) {
|
|
4799
|
-
return
|
|
4800
|
-
{ error: err2 instanceof Error ? err2.message : "Export failed" },
|
|
4801
|
-
500
|
|
4802
|
-
);
|
|
4699
|
+
return json({ error: err2 instanceof Error ? err2.message : "Export failed" }, 500);
|
|
4803
4700
|
}
|
|
4804
4701
|
}
|
|
4805
4702
|
});
|
|
@@ -4813,30 +4710,28 @@ function gdpr() {
|
|
|
4813
4710
|
async handler(request, endpointCtx) {
|
|
4814
4711
|
const user = await endpointCtx.getUser(request);
|
|
4815
4712
|
if (!user) {
|
|
4816
|
-
return
|
|
4713
|
+
return json({ error: "Authentication required" }, 401);
|
|
4817
4714
|
}
|
|
4818
|
-
const
|
|
4819
|
-
if (
|
|
4820
|
-
|
|
4715
|
+
const bodyResult = await parseBody(request);
|
|
4716
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
4717
|
+
if (bodyResult.data.confirm !== "delete my account") {
|
|
4718
|
+
return json(
|
|
4821
4719
|
{
|
|
4822
4720
|
error: 'Confirmation required. Send { "confirm": "delete my account" } in the request body.'
|
|
4823
4721
|
},
|
|
4824
4722
|
400
|
|
4825
4723
|
);
|
|
4826
4724
|
}
|
|
4827
|
-
const keepAuditLogs = typeof
|
|
4828
|
-
const deleteOrganizations = typeof
|
|
4725
|
+
const keepAuditLogs = typeof bodyResult.data.keepAuditLogs === "boolean" ? bodyResult.data.keepAuditLogs : true;
|
|
4726
|
+
const deleteOrganizations = typeof bodyResult.data.deleteOrganizations === "boolean" ? bodyResult.data.deleteOrganizations : false;
|
|
4829
4727
|
try {
|
|
4830
4728
|
const result = await module.deleteUser(user.id, {
|
|
4831
4729
|
keepAuditLogs,
|
|
4832
4730
|
deleteOrganizations
|
|
4833
4731
|
});
|
|
4834
|
-
return
|
|
4732
|
+
return json({ success: true, ...result });
|
|
4835
4733
|
} catch (err2) {
|
|
4836
|
-
return
|
|
4837
|
-
{ error: err2 instanceof Error ? err2.message : "Deletion failed" },
|
|
4838
|
-
500
|
|
4839
|
-
);
|
|
4734
|
+
return json({ error: err2 instanceof Error ? err2.message : "Deletion failed" }, 500);
|
|
4840
4735
|
}
|
|
4841
4736
|
}
|
|
4842
4737
|
});
|
|
@@ -4850,13 +4745,13 @@ function gdpr() {
|
|
|
4850
4745
|
async handler(request, endpointCtx) {
|
|
4851
4746
|
const user = await endpointCtx.getUser(request);
|
|
4852
4747
|
if (!user) {
|
|
4853
|
-
return
|
|
4748
|
+
return json({ error: "Authentication required" }, 401);
|
|
4854
4749
|
}
|
|
4855
4750
|
try {
|
|
4856
4751
|
await module.anonymizeUser(user.id);
|
|
4857
|
-
return
|
|
4752
|
+
return json({ success: true });
|
|
4858
4753
|
} catch (err2) {
|
|
4859
|
-
return
|
|
4754
|
+
return json(
|
|
4860
4755
|
{ error: err2 instanceof Error ? err2.message : "Anonymization failed" },
|
|
4861
4756
|
500
|
|
4862
4757
|
);
|
|
@@ -5492,14 +5387,12 @@ function magicLink(config) {
|
|
|
5492
5387
|
return {
|
|
5493
5388
|
id: "kavach-magic-link",
|
|
5494
5389
|
async init(ctx) {
|
|
5495
|
-
|
|
5496
|
-
if (!sessionConfig) {
|
|
5390
|
+
if (!ctx.sessionManager) {
|
|
5497
5391
|
throw new Error(
|
|
5498
5392
|
"kavach-magic-link plugin requires auth.session to be configured so that sessions can be issued on successful verification."
|
|
5499
5393
|
);
|
|
5500
5394
|
}
|
|
5501
|
-
const
|
|
5502
|
-
const module = createMagicLinkModule(config, ctx.db, sessionManager);
|
|
5395
|
+
const module = createMagicLinkModule(config, ctx.db, ctx.sessionManager);
|
|
5503
5396
|
const sendLimiter = createRateLimiter({ max: 5, window: 60 });
|
|
5504
5397
|
ctx.addEndpoint({
|
|
5505
5398
|
method: "POST",
|
|
@@ -5509,35 +5402,19 @@ function magicLink(config) {
|
|
|
5509
5402
|
description: "Send a magic link to the provided email address"
|
|
5510
5403
|
},
|
|
5511
5404
|
handler: withRateLimit(async (request) => {
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
} catch {
|
|
5516
|
-
return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
|
|
5517
|
-
status: 400,
|
|
5518
|
-
headers: { "Content-Type": "application/json" }
|
|
5519
|
-
});
|
|
5520
|
-
}
|
|
5521
|
-
const b = body;
|
|
5522
|
-
const rawEmail = typeof b.email === "string" ? b.email.trim().toLowerCase() : null;
|
|
5405
|
+
const bodyResult = await parseBody(request);
|
|
5406
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
5407
|
+
const rawEmail = typeof bodyResult.data.email === "string" ? bodyResult.data.email.trim().toLowerCase() : null;
|
|
5523
5408
|
if (!rawEmail) {
|
|
5524
|
-
return
|
|
5525
|
-
status: 400,
|
|
5526
|
-
headers: { "Content-Type": "application/json" }
|
|
5527
|
-
});
|
|
5409
|
+
return json({ error: "Missing required field: email" }, 400);
|
|
5528
5410
|
}
|
|
5529
5411
|
try {
|
|
5530
5412
|
const result = await module.sendLink(rawEmail);
|
|
5531
|
-
return
|
|
5532
|
-
status: 200,
|
|
5533
|
-
headers: { "Content-Type": "application/json" }
|
|
5534
|
-
});
|
|
5413
|
+
return json(result);
|
|
5535
5414
|
} catch (err2) {
|
|
5536
|
-
return
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
}),
|
|
5540
|
-
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
5415
|
+
return json(
|
|
5416
|
+
{ error: err2 instanceof Error ? err2.message : "Failed to send magic link" },
|
|
5417
|
+
500
|
|
5541
5418
|
);
|
|
5542
5419
|
}
|
|
5543
5420
|
}, sendLimiter)
|
|
@@ -5552,22 +5429,13 @@ function magicLink(config) {
|
|
|
5552
5429
|
const url = new URL(request.url);
|
|
5553
5430
|
const token = url.searchParams.get("token");
|
|
5554
5431
|
if (!token) {
|
|
5555
|
-
return
|
|
5556
|
-
status: 400,
|
|
5557
|
-
headers: { "Content-Type": "application/json" }
|
|
5558
|
-
});
|
|
5432
|
+
return json({ error: "Missing token query parameter" }, 400);
|
|
5559
5433
|
}
|
|
5560
5434
|
const result = await module.verify(token);
|
|
5561
5435
|
if (!result) {
|
|
5562
|
-
return
|
|
5563
|
-
status: 401,
|
|
5564
|
-
headers: { "Content-Type": "application/json" }
|
|
5565
|
-
});
|
|
5436
|
+
return json({ error: "Invalid or expired magic link" }, 401);
|
|
5566
5437
|
}
|
|
5567
|
-
return
|
|
5568
|
-
status: 200,
|
|
5569
|
-
headers: { "Content-Type": "application/json" }
|
|
5570
|
-
});
|
|
5438
|
+
return json(result);
|
|
5571
5439
|
}
|
|
5572
5440
|
});
|
|
5573
5441
|
}
|
|
@@ -5792,7 +5660,7 @@ function createOAuthModule(db, config) {
|
|
|
5792
5660
|
pruneExpiredStates
|
|
5793
5661
|
};
|
|
5794
5662
|
}
|
|
5795
|
-
function
|
|
5663
|
+
function jsonResponse5(body, status = 200) {
|
|
5796
5664
|
return new Response(JSON.stringify(body), {
|
|
5797
5665
|
status,
|
|
5798
5666
|
headers: { "Content-Type": "application/json" }
|
|
@@ -5810,8 +5678,12 @@ function oauth(config) {
|
|
|
5810
5678
|
async init(ctx) {
|
|
5811
5679
|
const module = createOAuthModule(ctx.db, config);
|
|
5812
5680
|
const baseUrl = ctx.config.baseUrl ?? "";
|
|
5813
|
-
const
|
|
5814
|
-
|
|
5681
|
+
const sessionManager = ctx.sessionManager;
|
|
5682
|
+
if (!sessionManager) {
|
|
5683
|
+
throw new Error(
|
|
5684
|
+
"kavach-oauth plugin requires auth.session to be configured so that sessions can be issued on successful OAuth callback."
|
|
5685
|
+
);
|
|
5686
|
+
}
|
|
5815
5687
|
const authorizeLimiter = createRateLimiter({ max: 20, window: 60 });
|
|
5816
5688
|
function getRedirectUri(provider) {
|
|
5817
5689
|
if (config.buildRedirectUri) {
|
|
@@ -5830,14 +5702,14 @@ function oauth(config) {
|
|
|
5830
5702
|
const url = new URL(request.url);
|
|
5831
5703
|
const provider = url.searchParams.get("_param_provider");
|
|
5832
5704
|
if (!provider) {
|
|
5833
|
-
return
|
|
5705
|
+
return jsonResponse5({ error: "Missing provider parameter" }, 400);
|
|
5834
5706
|
}
|
|
5835
5707
|
const redirectUri = getRedirectUri(provider);
|
|
5836
5708
|
try {
|
|
5837
5709
|
const { url: authUrl } = await module.getAuthorizationUrl(provider, redirectUri);
|
|
5838
5710
|
return redirectResponse(authUrl);
|
|
5839
5711
|
} catch (err2) {
|
|
5840
|
-
return
|
|
5712
|
+
return jsonResponse5(
|
|
5841
5713
|
{ error: err2 instanceof Error ? err2.message : "Failed to build authorization URL" },
|
|
5842
5714
|
400
|
|
5843
5715
|
);
|
|
@@ -5854,10 +5726,10 @@ function oauth(config) {
|
|
|
5854
5726
|
const code2 = url.searchParams.get("code");
|
|
5855
5727
|
const state = url.searchParams.get("state");
|
|
5856
5728
|
if (!provider) {
|
|
5857
|
-
return
|
|
5729
|
+
return jsonResponse5({ error: "Missing provider parameter" }, 400);
|
|
5858
5730
|
}
|
|
5859
5731
|
if (!code2 || !state) {
|
|
5860
|
-
return
|
|
5732
|
+
return jsonResponse5({ error: "Missing code or state query parameter" }, 400);
|
|
5861
5733
|
}
|
|
5862
5734
|
const redirectUri = getRedirectUri(provider);
|
|
5863
5735
|
try {
|
|
@@ -5889,18 +5761,27 @@ function oauth(config) {
|
|
|
5889
5761
|
raw: {}
|
|
5890
5762
|
});
|
|
5891
5763
|
}
|
|
5892
|
-
if (
|
|
5764
|
+
if (userId !== "__pending__") {
|
|
5893
5765
|
const { session, token } = await sessionManager.create(userId);
|
|
5894
|
-
const
|
|
5895
|
-
|
|
5766
|
+
const maxAge = Math.floor((session.expiresAt.getTime() - Date.now()) / 1e3);
|
|
5767
|
+
const cookie = buildSetCookie("kavach_session", token, maxAge);
|
|
5768
|
+
const userInfo = encodeURIComponent(JSON.stringify({ id: userId, email }));
|
|
5769
|
+
const callbackUrl = `${baseUrl}/?auth_user=${userInfo}`;
|
|
5770
|
+
return new Response(null, {
|
|
5771
|
+
status: 302,
|
|
5772
|
+
headers: {
|
|
5773
|
+
Location: callbackUrl,
|
|
5774
|
+
"Set-Cookie": cookie
|
|
5775
|
+
}
|
|
5776
|
+
});
|
|
5896
5777
|
}
|
|
5897
|
-
return
|
|
5778
|
+
return jsonResponse5({
|
|
5898
5779
|
isNewAccount: result.isNewAccount,
|
|
5899
5780
|
account: result.account,
|
|
5900
5781
|
userInfo: result.userInfo
|
|
5901
5782
|
});
|
|
5902
5783
|
} catch (err2) {
|
|
5903
|
-
return
|
|
5784
|
+
return jsonResponse5(
|
|
5904
5785
|
{ error: err2 instanceof Error ? err2.message : "OAuth callback failed" },
|
|
5905
5786
|
400
|
|
5906
5787
|
);
|
|
@@ -5917,29 +5798,29 @@ function oauth(config) {
|
|
|
5917
5798
|
async handler(request, endpointCtx) {
|
|
5918
5799
|
const user = await endpointCtx.getUser(request);
|
|
5919
5800
|
if (!user) {
|
|
5920
|
-
return
|
|
5801
|
+
return jsonResponse5({ error: "Authentication required" }, 401);
|
|
5921
5802
|
}
|
|
5922
5803
|
let body;
|
|
5923
5804
|
try {
|
|
5924
5805
|
body = await request.json();
|
|
5925
5806
|
} catch {
|
|
5926
|
-
return
|
|
5807
|
+
return jsonResponse5({ error: "Invalid JSON body" }, 400);
|
|
5927
5808
|
}
|
|
5928
5809
|
const b = body;
|
|
5929
5810
|
const provider = typeof b.provider === "string" ? b.provider : null;
|
|
5930
5811
|
const userInfo = typeof b.userInfo === "object" && b.userInfo !== null ? b.userInfo : null;
|
|
5931
5812
|
const tokens = typeof b.tokens === "object" && b.tokens !== null ? b.tokens : null;
|
|
5932
5813
|
if (!provider || !userInfo || !tokens) {
|
|
5933
|
-
return
|
|
5814
|
+
return jsonResponse5(
|
|
5934
5815
|
{ error: "Missing required fields: provider, userInfo, tokens" },
|
|
5935
5816
|
400
|
|
5936
5817
|
);
|
|
5937
5818
|
}
|
|
5938
5819
|
try {
|
|
5939
5820
|
const account = await module.linkAccount(user.id, provider, userInfo, tokens);
|
|
5940
|
-
return
|
|
5821
|
+
return jsonResponse5({ account });
|
|
5941
5822
|
} catch (err2) {
|
|
5942
|
-
return
|
|
5823
|
+
return jsonResponse5(
|
|
5943
5824
|
{ error: err2 instanceof Error ? err2.message : "Failed to link account" },
|
|
5944
5825
|
400
|
|
5945
5826
|
);
|
|
@@ -5955,7 +5836,7 @@ function oauth(config) {
|
|
|
5955
5836
|
id: p2.id,
|
|
5956
5837
|
name: p2.name
|
|
5957
5838
|
}));
|
|
5958
|
-
return
|
|
5839
|
+
return jsonResponse5({ providers });
|
|
5959
5840
|
}
|
|
5960
5841
|
});
|
|
5961
5842
|
}
|
|
@@ -7752,25 +7633,25 @@ function createOneTapModule(config, db, sessionManager) {
|
|
|
7752
7633
|
const text3 = await request.text();
|
|
7753
7634
|
formData = new URLSearchParams(text3);
|
|
7754
7635
|
} catch {
|
|
7755
|
-
return
|
|
7636
|
+
return jsonResponse6({ error: "Failed to parse request body" }, 400);
|
|
7756
7637
|
}
|
|
7757
7638
|
const credential = formData.get("credential");
|
|
7758
7639
|
const bodyToken = formData.get(csrfCookieName);
|
|
7759
7640
|
if (!credential) {
|
|
7760
|
-
return
|
|
7641
|
+
return jsonResponse6({ error: "Missing credential field" }, 400);
|
|
7761
7642
|
}
|
|
7762
7643
|
const cookieToken = getCsrfCookie(request);
|
|
7763
7644
|
if (!cookieToken || !bodyToken || cookieToken !== bodyToken) {
|
|
7764
|
-
return
|
|
7645
|
+
return jsonResponse6({ error: "CSRF token mismatch" }, 403);
|
|
7765
7646
|
}
|
|
7766
7647
|
let googleUser;
|
|
7767
7648
|
try {
|
|
7768
7649
|
googleUser = await verify(credential);
|
|
7769
7650
|
} catch (err2) {
|
|
7770
7651
|
if (err2 instanceof OneTapVerifyError && err2.code === "USER_NOT_FOUND") {
|
|
7771
|
-
return
|
|
7652
|
+
return jsonResponse6({ error: err2.message }, 403);
|
|
7772
7653
|
}
|
|
7773
|
-
return
|
|
7654
|
+
return jsonResponse6(
|
|
7774
7655
|
{ error: err2 instanceof Error ? err2.message : "Token verification failed" },
|
|
7775
7656
|
401
|
|
7776
7657
|
);
|
|
@@ -7780,27 +7661,96 @@ function createOneTapModule(config, db, sessionManager) {
|
|
|
7780
7661
|
user = await findOrCreateUser(googleUser);
|
|
7781
7662
|
} catch (err2) {
|
|
7782
7663
|
if (err2 instanceof OneTapVerifyError && err2.code === "USER_NOT_FOUND") {
|
|
7783
|
-
return
|
|
7664
|
+
return jsonResponse6({ error: err2.message }, 403);
|
|
7784
7665
|
}
|
|
7785
|
-
return
|
|
7666
|
+
return jsonResponse6(
|
|
7786
7667
|
{ error: err2 instanceof Error ? err2.message : "Failed to resolve user" },
|
|
7787
7668
|
500
|
|
7788
7669
|
);
|
|
7789
7670
|
}
|
|
7790
7671
|
const { token: sessionToken, session } = await sessionManager.create(user.id);
|
|
7791
|
-
return
|
|
7672
|
+
return jsonResponse6({
|
|
7792
7673
|
user: { id: user.id, email: user.email },
|
|
7793
7674
|
session: { token: sessionToken, expiresAt: session.expiresAt }
|
|
7794
7675
|
});
|
|
7795
7676
|
}
|
|
7796
7677
|
return { verify, handleRequest };
|
|
7797
7678
|
}
|
|
7798
|
-
function
|
|
7679
|
+
function jsonResponse6(body, status = 200) {
|
|
7799
7680
|
return new Response(JSON.stringify(body), {
|
|
7800
7681
|
status,
|
|
7801
7682
|
headers: { "Content-Type": "application/json" }
|
|
7802
7683
|
});
|
|
7803
7684
|
}
|
|
7685
|
+
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
|
|
7686
|
+
function createSessionManager(config, db) {
|
|
7687
|
+
if (!config.secret || config.secret.length < 32) {
|
|
7688
|
+
throw new Error("SessionManager: secret must be at least 32 characters.");
|
|
7689
|
+
}
|
|
7690
|
+
const maxAge = config.maxAge ?? DEFAULT_MAX_AGE_SECONDS;
|
|
7691
|
+
const keyObject = new TextEncoder().encode(config.secret);
|
|
7692
|
+
function rowToSession2(row) {
|
|
7693
|
+
return {
|
|
7694
|
+
id: row.id,
|
|
7695
|
+
userId: row.userId,
|
|
7696
|
+
expiresAt: row.expiresAt,
|
|
7697
|
+
createdAt: row.createdAt,
|
|
7698
|
+
...row.metadata !== null && { metadata: row.metadata }
|
|
7699
|
+
};
|
|
7700
|
+
}
|
|
7701
|
+
async function create(userId, metadata) {
|
|
7702
|
+
const id = generateId();
|
|
7703
|
+
const now = /* @__PURE__ */ new Date();
|
|
7704
|
+
const expiresAt = new Date(now.getTime() + maxAge * 1e3);
|
|
7705
|
+
await db.insert(sessions).values({
|
|
7706
|
+
id,
|
|
7707
|
+
userId,
|
|
7708
|
+
expiresAt,
|
|
7709
|
+
metadata: metadata ?? null,
|
|
7710
|
+
createdAt: now
|
|
7711
|
+
});
|
|
7712
|
+
const token = await new SignJWT({ sub: id }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(Math.floor(expiresAt.getTime() / 1e3)).sign(keyObject);
|
|
7713
|
+
const session = {
|
|
7714
|
+
id,
|
|
7715
|
+
userId,
|
|
7716
|
+
expiresAt,
|
|
7717
|
+
createdAt: now,
|
|
7718
|
+
...metadata !== void 0 && { metadata }
|
|
7719
|
+
};
|
|
7720
|
+
return { session, token };
|
|
7721
|
+
}
|
|
7722
|
+
async function validate(token) {
|
|
7723
|
+
let sessionId;
|
|
7724
|
+
try {
|
|
7725
|
+
const { payload } = await jwtVerify(token, keyObject);
|
|
7726
|
+
if (typeof payload.sub !== "string" || !payload.sub) return null;
|
|
7727
|
+
sessionId = payload.sub;
|
|
7728
|
+
} catch {
|
|
7729
|
+
return null;
|
|
7730
|
+
}
|
|
7731
|
+
const now = /* @__PURE__ */ new Date();
|
|
7732
|
+
const rows = await db.select().from(sessions).where(and(eq(sessions.id, sessionId)));
|
|
7733
|
+
const row = rows[0];
|
|
7734
|
+
if (!row) return null;
|
|
7735
|
+
if (row.expiresAt <= now) {
|
|
7736
|
+
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
|
7737
|
+
return null;
|
|
7738
|
+
}
|
|
7739
|
+
return rowToSession2(row);
|
|
7740
|
+
}
|
|
7741
|
+
async function revoke(sessionId) {
|
|
7742
|
+
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
|
7743
|
+
}
|
|
7744
|
+
async function revokeAll(userId) {
|
|
7745
|
+
await db.delete(sessions).where(eq(sessions.userId, userId));
|
|
7746
|
+
}
|
|
7747
|
+
async function list(userId) {
|
|
7748
|
+
const now = /* @__PURE__ */ new Date();
|
|
7749
|
+
const rows = await db.select().from(sessions).where(and(eq(sessions.userId, userId)));
|
|
7750
|
+
return rows.filter((row) => row.expiresAt > now).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).map(rowToSession2);
|
|
7751
|
+
}
|
|
7752
|
+
return { create, validate, revoke, revokeAll, list };
|
|
7753
|
+
}
|
|
7804
7754
|
|
|
7805
7755
|
// src/auth/one-tap-plugin.ts
|
|
7806
7756
|
function oneTap(config) {
|
|
@@ -9092,14 +9042,14 @@ function rowToInvitation(row) {
|
|
|
9092
9042
|
createdAt: row.createdAt
|
|
9093
9043
|
};
|
|
9094
9044
|
}
|
|
9095
|
-
function
|
|
9045
|
+
function jsonResponse7(body, status = 200) {
|
|
9096
9046
|
return new Response(JSON.stringify(body), {
|
|
9097
9047
|
status,
|
|
9098
9048
|
headers: { "Content-Type": "application/json" }
|
|
9099
9049
|
});
|
|
9100
9050
|
}
|
|
9101
9051
|
function errorResponse(message, status) {
|
|
9102
|
-
return
|
|
9052
|
+
return jsonResponse7({ error: message }, status);
|
|
9103
9053
|
}
|
|
9104
9054
|
function createOrgModule(config, db) {
|
|
9105
9055
|
const maxMembers = config.maxMembers ?? DEFAULT_MAX_MEMBERS;
|
|
@@ -9405,7 +9355,7 @@ function createOrgModule(config, db) {
|
|
|
9405
9355
|
try {
|
|
9406
9356
|
const body = await request.json();
|
|
9407
9357
|
const org = await create(body);
|
|
9408
|
-
return
|
|
9358
|
+
return jsonResponse7(org, 201);
|
|
9409
9359
|
} catch (err2) {
|
|
9410
9360
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9411
9361
|
}
|
|
@@ -9415,7 +9365,7 @@ function createOrgModule(config, db) {
|
|
|
9415
9365
|
const userId = userOrgMatch[1];
|
|
9416
9366
|
if (!userId) return errorResponse("Missing userId", 400);
|
|
9417
9367
|
const orgs = await list(userId);
|
|
9418
|
-
return
|
|
9368
|
+
return jsonResponse7(orgs);
|
|
9419
9369
|
}
|
|
9420
9370
|
const orgBaseMatch = pathname.match(/^\/auth\/org\/([^/]+)(\/.*)?$/);
|
|
9421
9371
|
if (!orgBaseMatch) return null;
|
|
@@ -9425,13 +9375,13 @@ function createOrgModule(config, db) {
|
|
|
9425
9375
|
if (method === "GET" && subPath === "") {
|
|
9426
9376
|
const org = await get(orgId);
|
|
9427
9377
|
if (!org) return errorResponse("Organization not found", 404);
|
|
9428
|
-
return
|
|
9378
|
+
return jsonResponse7(org);
|
|
9429
9379
|
}
|
|
9430
9380
|
if (method === "PATCH" && subPath === "") {
|
|
9431
9381
|
try {
|
|
9432
9382
|
const body = await request.json();
|
|
9433
9383
|
const org = await update(orgId, body);
|
|
9434
|
-
return
|
|
9384
|
+
return jsonResponse7(org);
|
|
9435
9385
|
} catch (err2) {
|
|
9436
9386
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9437
9387
|
}
|
|
@@ -9439,7 +9389,7 @@ function createOrgModule(config, db) {
|
|
|
9439
9389
|
if (method === "DELETE" && subPath === "") {
|
|
9440
9390
|
try {
|
|
9441
9391
|
await remove(orgId);
|
|
9442
|
-
return
|
|
9392
|
+
return jsonResponse7({ success: true });
|
|
9443
9393
|
} catch (err2) {
|
|
9444
9394
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9445
9395
|
}
|
|
@@ -9448,14 +9398,14 @@ function createOrgModule(config, db) {
|
|
|
9448
9398
|
try {
|
|
9449
9399
|
const body = await request.json();
|
|
9450
9400
|
const member = await addMember(orgId, body.userId, body.role);
|
|
9451
|
-
return
|
|
9401
|
+
return jsonResponse7(member, 201);
|
|
9452
9402
|
} catch (err2) {
|
|
9453
9403
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9454
9404
|
}
|
|
9455
9405
|
}
|
|
9456
9406
|
if (method === "GET" && subPath === "/members") {
|
|
9457
9407
|
const members = await getMembers(orgId);
|
|
9458
|
-
return
|
|
9408
|
+
return jsonResponse7(members);
|
|
9459
9409
|
}
|
|
9460
9410
|
const memberMatch = subPath.match(/^\/members\/([^/]+)$/);
|
|
9461
9411
|
if (method === "PATCH" && memberMatch) {
|
|
@@ -9464,7 +9414,7 @@ function createOrgModule(config, db) {
|
|
|
9464
9414
|
try {
|
|
9465
9415
|
const body = await request.json();
|
|
9466
9416
|
const member = await updateMemberRole(orgId, userId, body.role);
|
|
9467
|
-
return
|
|
9417
|
+
return jsonResponse7(member);
|
|
9468
9418
|
} catch (err2) {
|
|
9469
9419
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9470
9420
|
}
|
|
@@ -9473,20 +9423,20 @@ function createOrgModule(config, db) {
|
|
|
9473
9423
|
const userId = memberMatch[1];
|
|
9474
9424
|
if (!userId) return errorResponse("Missing userId", 400);
|
|
9475
9425
|
await removeMember(orgId, userId);
|
|
9476
|
-
return
|
|
9426
|
+
return jsonResponse7({ success: true });
|
|
9477
9427
|
}
|
|
9478
9428
|
if (method === "POST" && subPath === "/invite") {
|
|
9479
9429
|
try {
|
|
9480
9430
|
const body = await request.json();
|
|
9481
9431
|
const invitation = await invite({ orgId, ...body });
|
|
9482
|
-
return
|
|
9432
|
+
return jsonResponse7(invitation, 201);
|
|
9483
9433
|
} catch (err2) {
|
|
9484
9434
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9485
9435
|
}
|
|
9486
9436
|
}
|
|
9487
9437
|
if (method === "GET" && subPath === "/invitations") {
|
|
9488
9438
|
const invitations = await listInvitations(orgId);
|
|
9489
|
-
return
|
|
9439
|
+
return jsonResponse7(invitations);
|
|
9490
9440
|
}
|
|
9491
9441
|
const permMatch = subPath.match(/^\/permissions\/([^/]+)\/([^/]+)$/);
|
|
9492
9442
|
if (method === "GET" && permMatch) {
|
|
@@ -9494,20 +9444,20 @@ function createOrgModule(config, db) {
|
|
|
9494
9444
|
const permission = permMatch[2];
|
|
9495
9445
|
if (!userId || !permission) return errorResponse("Missing userId or permission", 400);
|
|
9496
9446
|
const allowed = await hasPermission(orgId, userId, permission);
|
|
9497
|
-
return
|
|
9447
|
+
return jsonResponse7({ allowed });
|
|
9498
9448
|
}
|
|
9499
9449
|
if (method === "POST" && subPath === "/roles") {
|
|
9500
9450
|
try {
|
|
9501
9451
|
const body = await request.json();
|
|
9502
9452
|
const role = await createRole(orgId, body);
|
|
9503
|
-
return
|
|
9453
|
+
return jsonResponse7(role, 201);
|
|
9504
9454
|
} catch (err2) {
|
|
9505
9455
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9506
9456
|
}
|
|
9507
9457
|
}
|
|
9508
9458
|
if (method === "GET" && subPath === "/roles") {
|
|
9509
9459
|
const roles = await getRoles(orgId);
|
|
9510
|
-
return
|
|
9460
|
+
return jsonResponse7(roles);
|
|
9511
9461
|
}
|
|
9512
9462
|
return null;
|
|
9513
9463
|
}
|
|
@@ -9522,7 +9472,7 @@ function createOrgModule(config, db) {
|
|
|
9522
9472
|
try {
|
|
9523
9473
|
const body = await request.json();
|
|
9524
9474
|
const member = await acceptInvitation(invitationId, body.userId);
|
|
9525
|
-
return
|
|
9475
|
+
return jsonResponse7(member, 201);
|
|
9526
9476
|
} catch (err2) {
|
|
9527
9477
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9528
9478
|
}
|
|
@@ -9532,7 +9482,7 @@ function createOrgModule(config, db) {
|
|
|
9532
9482
|
const invitationId = revokeMatch[1];
|
|
9533
9483
|
if (!invitationId) return errorResponse("Missing invitationId", 400);
|
|
9534
9484
|
await revokeInvitation(invitationId);
|
|
9535
|
-
return
|
|
9485
|
+
return jsonResponse7({ success: true });
|
|
9536
9486
|
}
|
|
9537
9487
|
return handleRequest(request);
|
|
9538
9488
|
}
|
|
@@ -9563,13 +9513,13 @@ function createOrgModule(config, db) {
|
|
|
9563
9513
|
}
|
|
9564
9514
|
|
|
9565
9515
|
// src/auth/organization-plugin.ts
|
|
9566
|
-
function
|
|
9516
|
+
function jsonResponse8(body, status = 200) {
|
|
9567
9517
|
return new Response(JSON.stringify(body), {
|
|
9568
9518
|
status,
|
|
9569
9519
|
headers: { "Content-Type": "application/json" }
|
|
9570
9520
|
});
|
|
9571
9521
|
}
|
|
9572
|
-
async function
|
|
9522
|
+
async function parseBody3(request) {
|
|
9573
9523
|
try {
|
|
9574
9524
|
return await request.json();
|
|
9575
9525
|
} catch {
|
|
@@ -9592,20 +9542,20 @@ function organization(config) {
|
|
|
9592
9542
|
async handler(request, endpointCtx) {
|
|
9593
9543
|
const user = await endpointCtx.getUser(request);
|
|
9594
9544
|
if (!user) {
|
|
9595
|
-
return
|
|
9545
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9596
9546
|
}
|
|
9597
|
-
const body = await
|
|
9547
|
+
const body = await parseBody3(request);
|
|
9598
9548
|
const name = typeof body.name === "string" ? body.name.trim() : null;
|
|
9599
9549
|
const slug = typeof body.slug === "string" ? body.slug.trim() : null;
|
|
9600
9550
|
if (!name || !slug) {
|
|
9601
|
-
return
|
|
9551
|
+
return jsonResponse8({ error: "Missing required fields: name, slug" }, 400);
|
|
9602
9552
|
}
|
|
9603
9553
|
const metadata = body.metadata !== void 0 && typeof body.metadata === "object" && body.metadata !== null ? body.metadata : void 0;
|
|
9604
9554
|
try {
|
|
9605
9555
|
const org = await module.create({ name, slug, ownerId: user.id, metadata });
|
|
9606
|
-
return
|
|
9556
|
+
return jsonResponse8(org, 201);
|
|
9607
9557
|
} catch (err2) {
|
|
9608
|
-
return
|
|
9558
|
+
return jsonResponse8(
|
|
9609
9559
|
{ error: err2 instanceof Error ? err2.message : "Failed to create organization" },
|
|
9610
9560
|
400
|
|
9611
9561
|
);
|
|
@@ -9622,10 +9572,10 @@ function organization(config) {
|
|
|
9622
9572
|
async handler(request, endpointCtx) {
|
|
9623
9573
|
const user = await endpointCtx.getUser(request);
|
|
9624
9574
|
if (!user) {
|
|
9625
|
-
return
|
|
9575
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9626
9576
|
}
|
|
9627
9577
|
const orgs = await module.list(user.id);
|
|
9628
|
-
return
|
|
9578
|
+
return jsonResponse8({ organizations: orgs });
|
|
9629
9579
|
}
|
|
9630
9580
|
});
|
|
9631
9581
|
ctx.addEndpoint({
|
|
@@ -9638,23 +9588,23 @@ function organization(config) {
|
|
|
9638
9588
|
async handler(request, endpointCtx) {
|
|
9639
9589
|
const user = await endpointCtx.getUser(request);
|
|
9640
9590
|
if (!user) {
|
|
9641
|
-
return
|
|
9591
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9642
9592
|
}
|
|
9643
9593
|
const url = new URL(request.url);
|
|
9644
9594
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9645
9595
|
const orgId = segments[2];
|
|
9646
9596
|
if (!orgId) {
|
|
9647
|
-
return
|
|
9597
|
+
return jsonResponse8({ error: "Missing organization ID in path" }, 400);
|
|
9648
9598
|
}
|
|
9649
9599
|
const member = await module.getMember(orgId, user.id);
|
|
9650
9600
|
if (!member || !ADMIN_ROLES.has(member.role)) {
|
|
9651
|
-
return
|
|
9601
|
+
return jsonResponse8({ error: "Admin or owner role required" }, 403);
|
|
9652
9602
|
}
|
|
9653
|
-
const body = await
|
|
9603
|
+
const body = await parseBody3(request);
|
|
9654
9604
|
const email = typeof body.email === "string" ? body.email.trim().toLowerCase() : null;
|
|
9655
9605
|
const role = typeof body.role === "string" ? body.role : "member";
|
|
9656
9606
|
if (!email) {
|
|
9657
|
-
return
|
|
9607
|
+
return jsonResponse8({ error: "Missing required field: email" }, 400);
|
|
9658
9608
|
}
|
|
9659
9609
|
try {
|
|
9660
9610
|
const invitation = await module.invite({
|
|
@@ -9663,9 +9613,9 @@ function organization(config) {
|
|
|
9663
9613
|
role,
|
|
9664
9614
|
invitedBy: user.id
|
|
9665
9615
|
});
|
|
9666
|
-
return
|
|
9616
|
+
return jsonResponse8(invitation, 201);
|
|
9667
9617
|
} catch (err2) {
|
|
9668
|
-
return
|
|
9618
|
+
return jsonResponse8(
|
|
9669
9619
|
{ error: err2 instanceof Error ? err2.message : "Failed to send invitation" },
|
|
9670
9620
|
400
|
|
9671
9621
|
);
|
|
@@ -9682,20 +9632,20 @@ function organization(config) {
|
|
|
9682
9632
|
async handler(request, endpointCtx) {
|
|
9683
9633
|
const user = await endpointCtx.getUser(request);
|
|
9684
9634
|
if (!user) {
|
|
9685
|
-
return
|
|
9635
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9686
9636
|
}
|
|
9687
9637
|
const url = new URL(request.url);
|
|
9688
9638
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9689
9639
|
const orgId = segments[2];
|
|
9690
9640
|
if (!orgId) {
|
|
9691
|
-
return
|
|
9641
|
+
return jsonResponse8({ error: "Missing organization ID in path" }, 400);
|
|
9692
9642
|
}
|
|
9693
9643
|
const callerMember = await module.getMember(orgId, user.id);
|
|
9694
9644
|
if (!callerMember) {
|
|
9695
|
-
return
|
|
9645
|
+
return jsonResponse8({ error: "You are not a member of this organization" }, 403);
|
|
9696
9646
|
}
|
|
9697
9647
|
const members = await module.getMembers(orgId);
|
|
9698
|
-
return
|
|
9648
|
+
return jsonResponse8({ members });
|
|
9699
9649
|
}
|
|
9700
9650
|
});
|
|
9701
9651
|
ctx.addEndpoint({
|
|
@@ -9708,29 +9658,29 @@ function organization(config) {
|
|
|
9708
9658
|
async handler(request, endpointCtx) {
|
|
9709
9659
|
const user = await endpointCtx.getUser(request);
|
|
9710
9660
|
if (!user) {
|
|
9711
|
-
return
|
|
9661
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9712
9662
|
}
|
|
9713
9663
|
const url = new URL(request.url);
|
|
9714
9664
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9715
9665
|
const orgId = segments[2];
|
|
9716
9666
|
const targetUserId = segments[4];
|
|
9717
9667
|
if (!orgId || !targetUserId) {
|
|
9718
|
-
return
|
|
9668
|
+
return jsonResponse8({ error: "Missing organization ID or user ID in path" }, 400);
|
|
9719
9669
|
}
|
|
9720
9670
|
const callerMember = await module.getMember(orgId, user.id);
|
|
9721
9671
|
if (!callerMember || !ADMIN_ROLES.has(callerMember.role)) {
|
|
9722
|
-
return
|
|
9672
|
+
return jsonResponse8({ error: "Admin or owner role required" }, 403);
|
|
9723
9673
|
}
|
|
9724
|
-
const body = await
|
|
9674
|
+
const body = await parseBody3(request);
|
|
9725
9675
|
const role = typeof body.role === "string" ? body.role : null;
|
|
9726
9676
|
if (!role) {
|
|
9727
|
-
return
|
|
9677
|
+
return jsonResponse8({ error: "Missing required field: role" }, 400);
|
|
9728
9678
|
}
|
|
9729
9679
|
try {
|
|
9730
9680
|
const member = await module.updateMemberRole(orgId, targetUserId, role);
|
|
9731
|
-
return
|
|
9681
|
+
return jsonResponse8(member);
|
|
9732
9682
|
} catch (err2) {
|
|
9733
|
-
return
|
|
9683
|
+
return jsonResponse8(
|
|
9734
9684
|
{ error: err2 instanceof Error ? err2.message : "Failed to update member role" },
|
|
9735
9685
|
400
|
|
9736
9686
|
);
|
|
@@ -9747,24 +9697,24 @@ function organization(config) {
|
|
|
9747
9697
|
async handler(request, endpointCtx) {
|
|
9748
9698
|
const user = await endpointCtx.getUser(request);
|
|
9749
9699
|
if (!user) {
|
|
9750
|
-
return
|
|
9700
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9751
9701
|
}
|
|
9752
9702
|
const url = new URL(request.url);
|
|
9753
9703
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9754
9704
|
const orgId = segments[2];
|
|
9755
9705
|
const targetUserId = segments[4];
|
|
9756
9706
|
if (!orgId || !targetUserId) {
|
|
9757
|
-
return
|
|
9707
|
+
return jsonResponse8({ error: "Missing organization ID or user ID in path" }, 400);
|
|
9758
9708
|
}
|
|
9759
9709
|
const callerMember = await module.getMember(orgId, user.id);
|
|
9760
9710
|
if (!callerMember || !ADMIN_ROLES.has(callerMember.role)) {
|
|
9761
|
-
return
|
|
9711
|
+
return jsonResponse8({ error: "Admin or owner role required" }, 403);
|
|
9762
9712
|
}
|
|
9763
9713
|
try {
|
|
9764
9714
|
await module.removeMember(orgId, targetUserId);
|
|
9765
|
-
return
|
|
9715
|
+
return jsonResponse8({ removed: true });
|
|
9766
9716
|
} catch (err2) {
|
|
9767
|
-
return
|
|
9717
|
+
return jsonResponse8(
|
|
9768
9718
|
{ error: err2 instanceof Error ? err2.message : "Failed to remove member" },
|
|
9769
9719
|
400
|
|
9770
9720
|
);
|
|
@@ -10169,13 +10119,13 @@ function parseAuthData(authData) {
|
|
|
10169
10119
|
}
|
|
10170
10120
|
return { rpIdHash, flags, signCount, attestedCredentialData };
|
|
10171
10121
|
}
|
|
10172
|
-
function
|
|
10122
|
+
function jsonResponse9(body, status = 200) {
|
|
10173
10123
|
return new Response(JSON.stringify(body), {
|
|
10174
10124
|
status,
|
|
10175
10125
|
headers: { "Content-Type": "application/json" }
|
|
10176
10126
|
});
|
|
10177
10127
|
}
|
|
10178
|
-
async function
|
|
10128
|
+
async function parseBody4(request) {
|
|
10179
10129
|
try {
|
|
10180
10130
|
return await request.json();
|
|
10181
10131
|
} catch {
|
|
@@ -10513,84 +10463,84 @@ function createPasskeyModule(config, db) {
|
|
|
10513
10463
|
const method = request.method.toUpperCase();
|
|
10514
10464
|
const segments = getPathSegments(url);
|
|
10515
10465
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "register" && segments[3] === "options") {
|
|
10516
|
-
const body = await
|
|
10466
|
+
const body = await parseBody4(request);
|
|
10517
10467
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
10518
10468
|
const userName = typeof body.userName === "string" ? body.userName : null;
|
|
10519
10469
|
if (!userId || !userName) {
|
|
10520
|
-
return
|
|
10470
|
+
return jsonResponse9({ error: "userId and userName required" }, 400);
|
|
10521
10471
|
}
|
|
10522
10472
|
try {
|
|
10523
10473
|
const options = await getRegistrationOptions(userId, userName);
|
|
10524
|
-
return
|
|
10474
|
+
return jsonResponse9(options);
|
|
10525
10475
|
} catch (err2) {
|
|
10526
10476
|
const message = err2 instanceof Error ? err2.message : "Failed to generate options";
|
|
10527
10477
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10528
|
-
return
|
|
10478
|
+
return jsonResponse9({ error: message, code: code2 }, 500);
|
|
10529
10479
|
}
|
|
10530
10480
|
}
|
|
10531
10481
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "register" && segments[3] === "verify") {
|
|
10532
|
-
const body = await
|
|
10482
|
+
const body = await parseBody4(request);
|
|
10533
10483
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
10534
|
-
if (!userId) return
|
|
10484
|
+
if (!userId) return jsonResponse9({ error: "userId required" }, 400);
|
|
10535
10485
|
const resp = body.response;
|
|
10536
|
-
if (!resp) return
|
|
10486
|
+
if (!resp) return jsonResponse9({ error: "response required" }, 400);
|
|
10537
10487
|
try {
|
|
10538
10488
|
const result = await verifyRegistration(userId, resp);
|
|
10539
|
-
return
|
|
10489
|
+
return jsonResponse9(result);
|
|
10540
10490
|
} catch (err2) {
|
|
10541
10491
|
const message = err2 instanceof Error ? err2.message : "Registration failed";
|
|
10542
10492
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10543
|
-
return
|
|
10493
|
+
return jsonResponse9({ error: message, code: code2 }, 400);
|
|
10544
10494
|
}
|
|
10545
10495
|
}
|
|
10546
10496
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "login" && segments[3] === "options") {
|
|
10547
|
-
const body = await
|
|
10497
|
+
const body = await parseBody4(request);
|
|
10548
10498
|
const userId = typeof body.userId === "string" ? body.userId : void 0;
|
|
10549
10499
|
try {
|
|
10550
10500
|
const options = await getAuthenticationOptions(userId);
|
|
10551
|
-
return
|
|
10501
|
+
return jsonResponse9(options);
|
|
10552
10502
|
} catch (err2) {
|
|
10553
10503
|
const message = err2 instanceof Error ? err2.message : "Failed to generate options";
|
|
10554
10504
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10555
|
-
return
|
|
10505
|
+
return jsonResponse9({ error: message, code: code2 }, 500);
|
|
10556
10506
|
}
|
|
10557
10507
|
}
|
|
10558
10508
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "login" && segments[3] === "verify") {
|
|
10559
|
-
const body = await
|
|
10509
|
+
const body = await parseBody4(request);
|
|
10560
10510
|
const resp = body.response;
|
|
10561
|
-
if (!resp) return
|
|
10511
|
+
if (!resp) return jsonResponse9({ error: "response required" }, 400);
|
|
10562
10512
|
try {
|
|
10563
10513
|
const result = await verifyAuthentication(resp);
|
|
10564
|
-
return
|
|
10514
|
+
return jsonResponse9(result);
|
|
10565
10515
|
} catch (err2) {
|
|
10566
10516
|
const message = err2 instanceof Error ? err2.message : "Authentication failed";
|
|
10567
10517
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10568
|
-
return
|
|
10518
|
+
return jsonResponse9({ error: message, code: code2 }, 401);
|
|
10569
10519
|
}
|
|
10570
10520
|
}
|
|
10571
10521
|
if (method === "GET" && segments.length === 3 && segments[1] === "passkey" && segments[2] === "credentials") {
|
|
10572
10522
|
const userId = url.searchParams.get("userId");
|
|
10573
|
-
if (!userId) return
|
|
10523
|
+
if (!userId) return jsonResponse9({ error: "userId query param required" }, 400);
|
|
10574
10524
|
try {
|
|
10575
10525
|
const creds = await listCredentials(userId);
|
|
10576
|
-
return
|
|
10526
|
+
return jsonResponse9({ credentials: creds });
|
|
10577
10527
|
} catch (err2) {
|
|
10578
10528
|
const message = err2 instanceof Error ? err2.message : "Failed to list credentials";
|
|
10579
|
-
return
|
|
10529
|
+
return jsonResponse9({ error: message }, 500);
|
|
10580
10530
|
}
|
|
10581
10531
|
}
|
|
10582
10532
|
if (method === "DELETE" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "credentials") {
|
|
10583
10533
|
const credentialId = segments[3];
|
|
10584
|
-
if (!credentialId) return
|
|
10585
|
-
const body = await
|
|
10534
|
+
if (!credentialId) return jsonResponse9({ error: "Credential ID required" }, 400);
|
|
10535
|
+
const body = await parseBody4(request);
|
|
10586
10536
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
10587
|
-
if (!userId) return
|
|
10537
|
+
if (!userId) return jsonResponse9({ error: "userId required" }, 400);
|
|
10588
10538
|
try {
|
|
10589
10539
|
await removeCredential(credentialId, userId);
|
|
10590
|
-
return
|
|
10540
|
+
return jsonResponse9({ removed: true });
|
|
10591
10541
|
} catch (err2) {
|
|
10592
10542
|
const message = err2 instanceof Error ? err2.message : "Failed to remove credential";
|
|
10593
|
-
return
|
|
10543
|
+
return jsonResponse9({ error: message }, 500);
|
|
10594
10544
|
}
|
|
10595
10545
|
}
|
|
10596
10546
|
return null;
|
|
@@ -10607,13 +10557,13 @@ function createPasskeyModule(config, db) {
|
|
|
10607
10557
|
}
|
|
10608
10558
|
|
|
10609
10559
|
// src/auth/passkey-plugin.ts
|
|
10610
|
-
function
|
|
10560
|
+
function jsonResponse10(body, status = 200) {
|
|
10611
10561
|
return new Response(JSON.stringify(body), {
|
|
10612
10562
|
status,
|
|
10613
10563
|
headers: { "Content-Type": "application/json" }
|
|
10614
10564
|
});
|
|
10615
10565
|
}
|
|
10616
|
-
async function
|
|
10566
|
+
async function parseBody5(request) {
|
|
10617
10567
|
try {
|
|
10618
10568
|
return await request.json();
|
|
10619
10569
|
} catch {
|
|
@@ -10635,16 +10585,16 @@ function passkey(config) {
|
|
|
10635
10585
|
async handler(request, endpointCtx) {
|
|
10636
10586
|
const user = await endpointCtx.getUser(request);
|
|
10637
10587
|
if (!user) {
|
|
10638
|
-
return
|
|
10588
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10639
10589
|
}
|
|
10640
|
-
const body = await
|
|
10590
|
+
const body = await parseBody5(request);
|
|
10641
10591
|
const userId = typeof body.userId === "string" ? body.userId : user.id;
|
|
10642
10592
|
const userName = typeof body.userName === "string" ? body.userName : user.email ?? user.id;
|
|
10643
10593
|
try {
|
|
10644
10594
|
const options = await module.getRegistrationOptions(userId, userName);
|
|
10645
|
-
return
|
|
10595
|
+
return jsonResponse10(options);
|
|
10646
10596
|
} catch (err2) {
|
|
10647
|
-
return
|
|
10597
|
+
return jsonResponse10(
|
|
10648
10598
|
{ error: err2 instanceof Error ? err2.message : "Failed to generate options" },
|
|
10649
10599
|
500
|
|
10650
10600
|
);
|
|
@@ -10661,19 +10611,19 @@ function passkey(config) {
|
|
|
10661
10611
|
async handler(request, endpointCtx) {
|
|
10662
10612
|
const user = await endpointCtx.getUser(request);
|
|
10663
10613
|
if (!user) {
|
|
10664
|
-
return
|
|
10614
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10665
10615
|
}
|
|
10666
|
-
const body = await
|
|
10616
|
+
const body = await parseBody5(request);
|
|
10667
10617
|
const userId = typeof body.userId === "string" ? body.userId : user.id;
|
|
10668
10618
|
const response = body.response;
|
|
10669
10619
|
if (!response) {
|
|
10670
|
-
return
|
|
10620
|
+
return jsonResponse10({ error: "Missing required field: response" }, 400);
|
|
10671
10621
|
}
|
|
10672
10622
|
try {
|
|
10673
10623
|
const result = await module.verifyRegistration(userId, response);
|
|
10674
|
-
return
|
|
10624
|
+
return jsonResponse10(result);
|
|
10675
10625
|
} catch (err2) {
|
|
10676
|
-
return
|
|
10626
|
+
return jsonResponse10(
|
|
10677
10627
|
{ error: err2 instanceof Error ? err2.message : "Registration failed" },
|
|
10678
10628
|
400
|
|
10679
10629
|
);
|
|
@@ -10687,13 +10637,13 @@ function passkey(config) {
|
|
|
10687
10637
|
description: "Get WebAuthn authentication options"
|
|
10688
10638
|
},
|
|
10689
10639
|
async handler(request) {
|
|
10690
|
-
const body = await
|
|
10640
|
+
const body = await parseBody5(request);
|
|
10691
10641
|
const userId = typeof body.userId === "string" ? body.userId : void 0;
|
|
10692
10642
|
try {
|
|
10693
10643
|
const options = await module.getAuthenticationOptions(userId);
|
|
10694
|
-
return
|
|
10644
|
+
return jsonResponse10(options);
|
|
10695
10645
|
} catch (err2) {
|
|
10696
|
-
return
|
|
10646
|
+
return jsonResponse10(
|
|
10697
10647
|
{ error: err2 instanceof Error ? err2.message : "Failed to generate options" },
|
|
10698
10648
|
500
|
|
10699
10649
|
);
|
|
@@ -10707,19 +10657,37 @@ function passkey(config) {
|
|
|
10707
10657
|
description: "Verify a WebAuthn assertion and return the authenticated user"
|
|
10708
10658
|
},
|
|
10709
10659
|
async handler(request) {
|
|
10710
|
-
const body = await
|
|
10660
|
+
const body = await parseBody5(request);
|
|
10711
10661
|
const response = body.response;
|
|
10712
10662
|
if (!response) {
|
|
10713
|
-
return
|
|
10663
|
+
return jsonResponse10({ error: "Missing required field: response" }, 400);
|
|
10714
10664
|
}
|
|
10715
10665
|
try {
|
|
10716
10666
|
const result = await module.verifyAuthentication(response);
|
|
10717
10667
|
if (!result) {
|
|
10718
|
-
return
|
|
10668
|
+
return jsonResponse10({ error: "Authentication failed" }, 401);
|
|
10719
10669
|
}
|
|
10720
|
-
|
|
10670
|
+
if (ctx.sessionManager) {
|
|
10671
|
+
const { session, token } = await ctx.sessionManager.create(result.userId);
|
|
10672
|
+
const maxAge = Math.floor((session.expiresAt.getTime() - Date.now()) / 1e3);
|
|
10673
|
+
return new Response(
|
|
10674
|
+
JSON.stringify({
|
|
10675
|
+
user: { id: result.userId },
|
|
10676
|
+
session: { token, expiresAt: session.expiresAt },
|
|
10677
|
+
credential: result.credential
|
|
10678
|
+
}),
|
|
10679
|
+
{
|
|
10680
|
+
status: 200,
|
|
10681
|
+
headers: {
|
|
10682
|
+
"Content-Type": "application/json",
|
|
10683
|
+
"Set-Cookie": buildSetCookie("kavach_session", token, maxAge)
|
|
10684
|
+
}
|
|
10685
|
+
}
|
|
10686
|
+
);
|
|
10687
|
+
}
|
|
10688
|
+
return jsonResponse10(result);
|
|
10721
10689
|
} catch (err2) {
|
|
10722
|
-
return
|
|
10690
|
+
return jsonResponse10(
|
|
10723
10691
|
{ error: err2 instanceof Error ? err2.message : "Authentication failed" },
|
|
10724
10692
|
401
|
|
10725
10693
|
);
|
|
@@ -10736,13 +10704,13 @@ function passkey(config) {
|
|
|
10736
10704
|
async handler(request, endpointCtx) {
|
|
10737
10705
|
const user = await endpointCtx.getUser(request);
|
|
10738
10706
|
if (!user) {
|
|
10739
|
-
return
|
|
10707
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10740
10708
|
}
|
|
10741
10709
|
try {
|
|
10742
10710
|
const credentials = await module.listCredentials(user.id);
|
|
10743
|
-
return
|
|
10711
|
+
return jsonResponse10({ credentials });
|
|
10744
10712
|
} catch (err2) {
|
|
10745
|
-
return
|
|
10713
|
+
return jsonResponse10(
|
|
10746
10714
|
{ error: err2 instanceof Error ? err2.message : "Failed to list credentials" },
|
|
10747
10715
|
500
|
|
10748
10716
|
);
|
|
@@ -10759,19 +10727,19 @@ function passkey(config) {
|
|
|
10759
10727
|
async handler(request, endpointCtx) {
|
|
10760
10728
|
const user = await endpointCtx.getUser(request);
|
|
10761
10729
|
if (!user) {
|
|
10762
|
-
return
|
|
10730
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10763
10731
|
}
|
|
10764
10732
|
const url = new URL(request.url);
|
|
10765
10733
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
10766
10734
|
const credentialId = segments[3];
|
|
10767
10735
|
if (!credentialId) {
|
|
10768
|
-
return
|
|
10736
|
+
return jsonResponse10({ error: "Missing credential ID in path" }, 400);
|
|
10769
10737
|
}
|
|
10770
10738
|
try {
|
|
10771
10739
|
await module.removeCredential(credentialId, user.id);
|
|
10772
|
-
return
|
|
10740
|
+
return jsonResponse10({ removed: true });
|
|
10773
10741
|
} catch (err2) {
|
|
10774
|
-
return
|
|
10742
|
+
return jsonResponse10(
|
|
10775
10743
|
{ error: err2 instanceof Error ? err2.message : "Failed to remove credential" },
|
|
10776
10744
|
500
|
|
10777
10745
|
);
|
|
@@ -10788,7 +10756,7 @@ var TOKEN_PURPOSE2 = "password-reset";
|
|
|
10788
10756
|
function makeError7(code2, message, details) {
|
|
10789
10757
|
return { code: code2, message, ...details !== void 0 ? { details } : {} };
|
|
10790
10758
|
}
|
|
10791
|
-
function
|
|
10759
|
+
function jsonResponse11(body, status = 200) {
|
|
10792
10760
|
return new Response(JSON.stringify(body), {
|
|
10793
10761
|
status,
|
|
10794
10762
|
headers: { "Content-Type": "application/json" }
|
|
@@ -10904,27 +10872,27 @@ function createPasswordResetModule(config, db, sessionManager, tokenModule) {
|
|
|
10904
10872
|
try {
|
|
10905
10873
|
body = await request.json();
|
|
10906
10874
|
} catch {
|
|
10907
|
-
return
|
|
10875
|
+
return jsonResponse11({ error: "Invalid JSON body" }, 400);
|
|
10908
10876
|
}
|
|
10909
10877
|
const b = body;
|
|
10910
10878
|
if (pathname === "/auth/forgot-password") {
|
|
10911
10879
|
if (typeof b.email !== "string") {
|
|
10912
|
-
return
|
|
10880
|
+
return jsonResponse11({ error: "Missing required field: email" }, 400);
|
|
10913
10881
|
}
|
|
10914
10882
|
const result = await requestReset(b.email);
|
|
10915
10883
|
if (!result.success) {
|
|
10916
|
-
return
|
|
10884
|
+
return jsonResponse11({ error: result.error.message }, 500);
|
|
10917
10885
|
}
|
|
10918
10886
|
return new Response(null, { status: 204 });
|
|
10919
10887
|
}
|
|
10920
10888
|
if (pathname === "/auth/reset-password") {
|
|
10921
10889
|
if (typeof b.token !== "string" || typeof b.password !== "string") {
|
|
10922
|
-
return
|
|
10890
|
+
return jsonResponse11({ error: "Missing required fields: token, password" }, 400);
|
|
10923
10891
|
}
|
|
10924
10892
|
const result = await resetPassword(b.token, b.password);
|
|
10925
10893
|
if (!result.success) {
|
|
10926
10894
|
const status = result.error.code === "INVALID_PASSWORD" ? 400 : 400;
|
|
10927
|
-
return
|
|
10895
|
+
return jsonResponse11({ error: result.error.message }, status);
|
|
10928
10896
|
}
|
|
10929
10897
|
return new Response(null, { status: 204 });
|
|
10930
10898
|
}
|
|
@@ -10959,7 +10927,7 @@ function generateNumericCode2(length) {
|
|
|
10959
10927
|
function normalisePhone(phone) {
|
|
10960
10928
|
return phone.replace(/\s+/g, "");
|
|
10961
10929
|
}
|
|
10962
|
-
function
|
|
10930
|
+
function jsonResponse12(body, status = 200) {
|
|
10963
10931
|
return new Response(JSON.stringify(body), {
|
|
10964
10932
|
status,
|
|
10965
10933
|
headers: { "Content-Type": "application/json" }
|
|
@@ -11031,25 +10999,25 @@ function createPhoneAuthModule(config, db, sessionManager) {
|
|
|
11031
10999
|
try {
|
|
11032
11000
|
body = await request.json();
|
|
11033
11001
|
} catch {
|
|
11034
|
-
return
|
|
11002
|
+
return jsonResponse12({ error: "Invalid JSON body" }, 400);
|
|
11035
11003
|
}
|
|
11036
11004
|
const b = body;
|
|
11037
11005
|
if (pathname === "/auth/phone/send-code") {
|
|
11038
11006
|
if (typeof b.phoneNumber !== "string") {
|
|
11039
|
-
return
|
|
11007
|
+
return jsonResponse12({ error: "Missing required field: phoneNumber" }, 400);
|
|
11040
11008
|
}
|
|
11041
11009
|
const result = await sendCode(b.phoneNumber);
|
|
11042
|
-
return
|
|
11010
|
+
return jsonResponse12(result);
|
|
11043
11011
|
}
|
|
11044
11012
|
if (pathname === "/auth/phone/verify") {
|
|
11045
11013
|
if (typeof b.phoneNumber !== "string" || typeof b.code !== "string") {
|
|
11046
|
-
return
|
|
11014
|
+
return jsonResponse12({ error: "Missing required fields: phoneNumber, code" }, 400);
|
|
11047
11015
|
}
|
|
11048
11016
|
const result = await verifyCode(b.phoneNumber, b.code);
|
|
11049
11017
|
if (!result) {
|
|
11050
|
-
return
|
|
11018
|
+
return jsonResponse12({ error: "Invalid or expired code" }, 401);
|
|
11051
11019
|
}
|
|
11052
|
-
return
|
|
11020
|
+
return jsonResponse12(result);
|
|
11053
11021
|
}
|
|
11054
11022
|
return null;
|
|
11055
11023
|
}
|
|
@@ -11069,12 +11037,12 @@ async function polarRequest(accessToken, baseUrl, method, path, body) {
|
|
|
11069
11037
|
headers,
|
|
11070
11038
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
11071
11039
|
});
|
|
11072
|
-
const
|
|
11040
|
+
const json2 = await response.json();
|
|
11073
11041
|
if (!response.ok) {
|
|
11074
|
-
const message =
|
|
11042
|
+
const message = json2.detail ?? json2.message ?? `Polar API error: ${response.status}`;
|
|
11075
11043
|
throw new Error(message);
|
|
11076
11044
|
}
|
|
11077
|
-
return
|
|
11045
|
+
return json2;
|
|
11078
11046
|
}
|
|
11079
11047
|
async function verifyWebhookSignature(payload, signatureHeader, webhookSecret) {
|
|
11080
11048
|
const prefix = "sha256=";
|
|
@@ -11297,19 +11265,6 @@ function createPolarModule(config, db) {
|
|
|
11297
11265
|
}
|
|
11298
11266
|
|
|
11299
11267
|
// src/auth/polar-plugin.ts
|
|
11300
|
-
function json(body, status = 200) {
|
|
11301
|
-
return new Response(JSON.stringify(body), {
|
|
11302
|
-
status,
|
|
11303
|
-
headers: { "Content-Type": "application/json" }
|
|
11304
|
-
});
|
|
11305
|
-
}
|
|
11306
|
-
async function parseBody10(request) {
|
|
11307
|
-
try {
|
|
11308
|
-
return await request.json();
|
|
11309
|
-
} catch {
|
|
11310
|
-
return {};
|
|
11311
|
-
}
|
|
11312
|
-
}
|
|
11313
11268
|
function polar(config) {
|
|
11314
11269
|
return {
|
|
11315
11270
|
id: "kavach-polar",
|
|
@@ -11327,13 +11282,14 @@ function polar(config) {
|
|
|
11327
11282
|
if (!user) {
|
|
11328
11283
|
return json({ error: "Authentication required" }, 401);
|
|
11329
11284
|
}
|
|
11330
|
-
const
|
|
11331
|
-
|
|
11285
|
+
const bodyResult = await parseBody(request);
|
|
11286
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
11287
|
+
const productId = typeof bodyResult.data.productId === "string" ? bodyResult.data.productId.trim() : null;
|
|
11332
11288
|
if (!productId) {
|
|
11333
11289
|
return json({ error: "Missing required field: productId" }, 400);
|
|
11334
11290
|
}
|
|
11335
|
-
const successUrl = typeof
|
|
11336
|
-
const customerEmail = typeof
|
|
11291
|
+
const successUrl = typeof bodyResult.data.successUrl === "string" ? bodyResult.data.successUrl : void 0;
|
|
11292
|
+
const customerEmail = typeof bodyResult.data.customerEmail === "string" ? bodyResult.data.customerEmail : void 0;
|
|
11337
11293
|
try {
|
|
11338
11294
|
const result = await module.createCheckout(user.id, productId, {
|
|
11339
11295
|
successUrl,
|
|
@@ -12778,13 +12734,13 @@ function scim(config) {
|
|
|
12778
12734
|
// src/auth/siwe.ts
|
|
12779
12735
|
var DEFAULT_NONCE_TTL_SECONDS = 300;
|
|
12780
12736
|
var SIWE_VERSION = "1";
|
|
12781
|
-
function
|
|
12737
|
+
function jsonResponse13(body, status = 200) {
|
|
12782
12738
|
return new Response(JSON.stringify(body), {
|
|
12783
12739
|
status,
|
|
12784
12740
|
headers: { "Content-Type": "application/json" }
|
|
12785
12741
|
});
|
|
12786
12742
|
}
|
|
12787
|
-
async function
|
|
12743
|
+
async function parseBody6(request) {
|
|
12788
12744
|
try {
|
|
12789
12745
|
return await request.json();
|
|
12790
12746
|
} catch {
|
|
@@ -12919,20 +12875,20 @@ function createSiweModule(config) {
|
|
|
12919
12875
|
const { method, pathname } = { method: request.method, pathname: url.pathname };
|
|
12920
12876
|
if (method === "GET" && pathname.endsWith("/auth/siwe/nonce")) {
|
|
12921
12877
|
const nonce = await generateNonce();
|
|
12922
|
-
return
|
|
12878
|
+
return jsonResponse13({ nonce });
|
|
12923
12879
|
}
|
|
12924
12880
|
if (method === "POST" && pathname.endsWith("/auth/siwe/verify")) {
|
|
12925
|
-
const body = await
|
|
12881
|
+
const body = await parseBody6(request);
|
|
12926
12882
|
const message = typeof body.message === "string" ? body.message : null;
|
|
12927
12883
|
const signature = typeof body.signature === "string" ? body.signature : null;
|
|
12928
12884
|
if (!message || !signature) {
|
|
12929
|
-
return
|
|
12885
|
+
return jsonResponse13({ error: "Missing required fields: message, signature" }, 400);
|
|
12930
12886
|
}
|
|
12931
12887
|
try {
|
|
12932
12888
|
const result = await verify(message, signature);
|
|
12933
|
-
return
|
|
12889
|
+
return jsonResponse13({ address: result.address, chainId: result.chainId });
|
|
12934
12890
|
} catch (err2) {
|
|
12935
|
-
return
|
|
12891
|
+
return jsonResponse13(
|
|
12936
12892
|
{ error: err2 instanceof Error ? err2.message : "Verification failed" },
|
|
12937
12893
|
400
|
|
12938
12894
|
);
|
|
@@ -13969,7 +13925,7 @@ function createSsoModule(config, db) {
|
|
|
13969
13925
|
const url = new URL(request.url);
|
|
13970
13926
|
const { pathname } = url;
|
|
13971
13927
|
const { method } = request;
|
|
13972
|
-
const
|
|
13928
|
+
const json2 = (data, status = 200) => new Response(JSON.stringify(data), {
|
|
13973
13929
|
status,
|
|
13974
13930
|
headers: { "Content-Type": "application/json" }
|
|
13975
13931
|
});
|
|
@@ -13978,14 +13934,14 @@ function createSsoModule(config, db) {
|
|
|
13978
13934
|
try {
|
|
13979
13935
|
body = await request.json();
|
|
13980
13936
|
} catch {
|
|
13981
|
-
return
|
|
13937
|
+
return json2({ error: "Invalid JSON body" }, 400);
|
|
13982
13938
|
}
|
|
13983
13939
|
const b = body;
|
|
13984
13940
|
if (typeof b.orgId !== "string" || typeof b.providerId !== "string" || typeof b.type !== "string" || typeof b.domain !== "string") {
|
|
13985
|
-
return
|
|
13941
|
+
return json2({ error: "Missing required fields: orgId, providerId, type, domain" }, 400);
|
|
13986
13942
|
}
|
|
13987
13943
|
if (b.type !== "saml" && b.type !== "oidc") {
|
|
13988
|
-
return
|
|
13944
|
+
return json2({ error: "type must be 'saml' or 'oidc'" }, 400);
|
|
13989
13945
|
}
|
|
13990
13946
|
const conn = await createConnection({
|
|
13991
13947
|
orgId: b.orgId,
|
|
@@ -13993,19 +13949,19 @@ function createSsoModule(config, db) {
|
|
|
13993
13949
|
type: b.type,
|
|
13994
13950
|
domain: b.domain
|
|
13995
13951
|
});
|
|
13996
|
-
return
|
|
13952
|
+
return json2(conn, 201);
|
|
13997
13953
|
}
|
|
13998
13954
|
const listMatch = /^\/auth\/sso\/connections\/([^/]+)$/.exec(pathname);
|
|
13999
13955
|
if (method === "GET" && listMatch) {
|
|
14000
13956
|
const orgId = decodeURIComponent(listMatch[1] ?? "");
|
|
14001
13957
|
const conns = await listConnections(orgId);
|
|
14002
|
-
return
|
|
13958
|
+
return json2(conns);
|
|
14003
13959
|
}
|
|
14004
13960
|
const deleteMatch = /^\/auth\/sso\/connections\/([^/]+)$/.exec(pathname);
|
|
14005
13961
|
if (method === "DELETE" && deleteMatch) {
|
|
14006
13962
|
const connId = decodeURIComponent(deleteMatch[1] ?? "");
|
|
14007
13963
|
await removeConnection(connId);
|
|
14008
|
-
return
|
|
13964
|
+
return json2({ success: true });
|
|
14009
13965
|
}
|
|
14010
13966
|
const samlInitMatch = /^\/auth\/sso\/saml\/([^/]+)$/.exec(pathname);
|
|
14011
13967
|
if (method === "GET" && samlInitMatch) {
|
|
@@ -14015,7 +13971,7 @@ function createSsoModule(config, db) {
|
|
|
14015
13971
|
const authUrl = await getSamlAuthUrl(connId, relayState);
|
|
14016
13972
|
return new Response(null, { status: 302, headers: { Location: authUrl } });
|
|
14017
13973
|
} catch (err2) {
|
|
14018
|
-
return
|
|
13974
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 400);
|
|
14019
13975
|
}
|
|
14020
13976
|
}
|
|
14021
13977
|
const samlAcsMatch = /^\/auth\/sso\/saml\/([^/]+)\/acs$/.exec(pathname);
|
|
@@ -14028,14 +13984,14 @@ function createSsoModule(config, db) {
|
|
|
14028
13984
|
if (typeof val !== "string") throw new Error("Missing SAMLResponse");
|
|
14029
13985
|
samlResponse = val;
|
|
14030
13986
|
} catch {
|
|
14031
|
-
return
|
|
13987
|
+
return json2({ error: "Missing or invalid SAMLResponse" }, 400);
|
|
14032
13988
|
}
|
|
14033
13989
|
try {
|
|
14034
13990
|
const result = await handleSamlResponse(connId, samlResponse);
|
|
14035
|
-
return
|
|
13991
|
+
return json2(result);
|
|
14036
13992
|
} catch (err2) {
|
|
14037
13993
|
const status = err2 instanceof SsoError && err2.code === SSO_ERROR.RATE_LIMITED ? 429 : 401;
|
|
14038
|
-
return
|
|
13994
|
+
return json2(
|
|
14039
13995
|
{
|
|
14040
13996
|
error: err2 instanceof Error ? err2.message : "SAML error",
|
|
14041
13997
|
code: err2 instanceof SsoError ? err2.code : "SAML_ERROR"
|
|
@@ -14053,20 +14009,20 @@ function createSsoModule(config, db) {
|
|
|
14053
14009
|
const authUrl = await getOidcAuthUrl(connId, state, nonce);
|
|
14054
14010
|
return new Response(null, { status: 302, headers: { Location: authUrl } });
|
|
14055
14011
|
} catch (err2) {
|
|
14056
|
-
return
|
|
14012
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 400);
|
|
14057
14013
|
}
|
|
14058
14014
|
}
|
|
14059
14015
|
const oidcCbMatch = /^\/auth\/sso\/oidc\/([^/]+)\/callback$/.exec(pathname);
|
|
14060
14016
|
if (method === "GET" && oidcCbMatch) {
|
|
14061
14017
|
const connId = decodeURIComponent(oidcCbMatch[1] ?? "");
|
|
14062
14018
|
const code2 = url.searchParams.get("code");
|
|
14063
|
-
if (!code2) return
|
|
14019
|
+
if (!code2) return json2({ error: "Missing code parameter" }, 400);
|
|
14064
14020
|
try {
|
|
14065
14021
|
const result = await handleOidcCallback(connId, code2);
|
|
14066
|
-
return
|
|
14022
|
+
return json2(result);
|
|
14067
14023
|
} catch (err2) {
|
|
14068
14024
|
const status = err2 instanceof SsoError && err2.code === SSO_ERROR.RATE_LIMITED ? 429 : 401;
|
|
14069
|
-
return
|
|
14025
|
+
return json2(
|
|
14070
14026
|
{
|
|
14071
14027
|
error: err2 instanceof Error ? err2.message : "OIDC error",
|
|
14072
14028
|
code: err2 instanceof SsoError ? err2.code : "OIDC_ERROR"
|
|
@@ -14167,12 +14123,12 @@ async function stripeRequest(secretKey, apiVersion, method, path, body) {
|
|
|
14167
14123
|
headers,
|
|
14168
14124
|
body: bodyStr
|
|
14169
14125
|
});
|
|
14170
|
-
const
|
|
14126
|
+
const json2 = await response.json();
|
|
14171
14127
|
if (!response.ok) {
|
|
14172
|
-
const message =
|
|
14128
|
+
const message = json2.error?.message ?? `Stripe API error: ${response.status}`;
|
|
14173
14129
|
throw new Error(message);
|
|
14174
14130
|
}
|
|
14175
|
-
return
|
|
14131
|
+
return json2;
|
|
14176
14132
|
}
|
|
14177
14133
|
async function verifyWebhookSignature2(payload, signatureHeader, webhookSecret) {
|
|
14178
14134
|
const parts = {};
|
|
@@ -14482,19 +14438,6 @@ function createStripeModule(config, db) {
|
|
|
14482
14438
|
}
|
|
14483
14439
|
|
|
14484
14440
|
// src/auth/stripe-plugin.ts
|
|
14485
|
-
function json2(body, status = 200) {
|
|
14486
|
-
return new Response(JSON.stringify(body), {
|
|
14487
|
-
status,
|
|
14488
|
-
headers: { "Content-Type": "application/json" }
|
|
14489
|
-
});
|
|
14490
|
-
}
|
|
14491
|
-
async function parseBody12(request) {
|
|
14492
|
-
try {
|
|
14493
|
-
return await request.json();
|
|
14494
|
-
} catch {
|
|
14495
|
-
return {};
|
|
14496
|
-
}
|
|
14497
|
-
}
|
|
14498
14441
|
function stripe(config) {
|
|
14499
14442
|
return {
|
|
14500
14443
|
id: "kavach-stripe",
|
|
@@ -14510,17 +14453,18 @@ function stripe(config) {
|
|
|
14510
14453
|
async handler(request, endpointCtx) {
|
|
14511
14454
|
const user = await endpointCtx.getUser(request);
|
|
14512
14455
|
if (!user) {
|
|
14513
|
-
return
|
|
14456
|
+
return json({ error: "Authentication required" }, 401);
|
|
14514
14457
|
}
|
|
14515
|
-
const
|
|
14516
|
-
|
|
14458
|
+
const bodyResult = await parseBody(request);
|
|
14459
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
14460
|
+
const priceId = typeof bodyResult.data.priceId === "string" ? bodyResult.data.priceId.trim() : null;
|
|
14517
14461
|
if (!priceId) {
|
|
14518
|
-
return
|
|
14462
|
+
return json({ error: "Missing required field: priceId" }, 400);
|
|
14519
14463
|
}
|
|
14520
|
-
const successUrl = typeof
|
|
14521
|
-
const cancelUrl = typeof
|
|
14522
|
-
const trialDays = typeof
|
|
14523
|
-
const metadata =
|
|
14464
|
+
const successUrl = typeof bodyResult.data.successUrl === "string" ? bodyResult.data.successUrl : void 0;
|
|
14465
|
+
const cancelUrl = typeof bodyResult.data.cancelUrl === "string" ? bodyResult.data.cancelUrl : void 0;
|
|
14466
|
+
const trialDays = typeof bodyResult.data.trialDays === "number" ? bodyResult.data.trialDays : void 0;
|
|
14467
|
+
const metadata = bodyResult.data.metadata != null && typeof bodyResult.data.metadata === "object" && !Array.isArray(bodyResult.data.metadata) ? bodyResult.data.metadata : void 0;
|
|
14524
14468
|
try {
|
|
14525
14469
|
const result = await module.createCheckoutSession(user.id, priceId, {
|
|
14526
14470
|
successUrl,
|
|
@@ -14528,9 +14472,9 @@ function stripe(config) {
|
|
|
14528
14472
|
trialDays,
|
|
14529
14473
|
metadata
|
|
14530
14474
|
});
|
|
14531
|
-
return
|
|
14475
|
+
return json(result);
|
|
14532
14476
|
} catch (err2) {
|
|
14533
|
-
return
|
|
14477
|
+
return json(
|
|
14534
14478
|
{
|
|
14535
14479
|
error: err2 instanceof Error ? err2.message : "Failed to create checkout session"
|
|
14536
14480
|
},
|
|
@@ -14549,18 +14493,19 @@ function stripe(config) {
|
|
|
14549
14493
|
async handler(request, endpointCtx) {
|
|
14550
14494
|
const user = await endpointCtx.getUser(request);
|
|
14551
14495
|
if (!user) {
|
|
14552
|
-
return
|
|
14496
|
+
return json({ error: "Authentication required" }, 401);
|
|
14553
14497
|
}
|
|
14554
|
-
const
|
|
14555
|
-
|
|
14498
|
+
const bodyResult = await parseBody(request);
|
|
14499
|
+
if (!bodyResult.ok) return bodyResult.response;
|
|
14500
|
+
const returnUrl = typeof bodyResult.data.returnUrl === "string" ? bodyResult.data.returnUrl.trim() : null;
|
|
14556
14501
|
if (!returnUrl) {
|
|
14557
|
-
return
|
|
14502
|
+
return json({ error: "Missing required field: returnUrl" }, 400);
|
|
14558
14503
|
}
|
|
14559
14504
|
try {
|
|
14560
14505
|
const result = await module.createPortalSession(user.id, returnUrl);
|
|
14561
|
-
return
|
|
14506
|
+
return json(result);
|
|
14562
14507
|
} catch (err2) {
|
|
14563
|
-
return
|
|
14508
|
+
return json(
|
|
14564
14509
|
{
|
|
14565
14510
|
error: err2 instanceof Error ? err2.message : "Failed to create portal session"
|
|
14566
14511
|
},
|
|
@@ -14579,10 +14524,10 @@ function stripe(config) {
|
|
|
14579
14524
|
async handler(request, endpointCtx) {
|
|
14580
14525
|
const user = await endpointCtx.getUser(request);
|
|
14581
14526
|
if (!user) {
|
|
14582
|
-
return
|
|
14527
|
+
return json({ error: "Authentication required" }, 401);
|
|
14583
14528
|
}
|
|
14584
14529
|
const subscription = await module.getSubscription(user.id);
|
|
14585
|
-
return
|
|
14530
|
+
return json({ subscription });
|
|
14586
14531
|
}
|
|
14587
14532
|
});
|
|
14588
14533
|
ctx.addEndpoint({
|
|
@@ -14684,13 +14629,13 @@ async function generateBackupCodes(count) {
|
|
|
14684
14629
|
}
|
|
14685
14630
|
return { plain, hashed };
|
|
14686
14631
|
}
|
|
14687
|
-
function
|
|
14632
|
+
function jsonResponse14(body, status = 200) {
|
|
14688
14633
|
return new Response(JSON.stringify(body), {
|
|
14689
14634
|
status,
|
|
14690
14635
|
headers: { "Content-Type": "application/json" }
|
|
14691
14636
|
});
|
|
14692
14637
|
}
|
|
14693
|
-
async function
|
|
14638
|
+
async function parseBody7(request) {
|
|
14694
14639
|
try {
|
|
14695
14640
|
return await request.json();
|
|
14696
14641
|
} catch {
|
|
@@ -14788,50 +14733,50 @@ function createTotpModule(config, db) {
|
|
|
14788
14733
|
const method = request.method.toUpperCase();
|
|
14789
14734
|
if (method !== "POST") return null;
|
|
14790
14735
|
if (path === "/auth/2fa/setup") {
|
|
14791
|
-
const body = await
|
|
14736
|
+
const body = await parseBody7(request);
|
|
14792
14737
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14793
|
-
if (!userId) return
|
|
14738
|
+
if (!userId) return jsonResponse14({ error: "userId required" }, 400);
|
|
14794
14739
|
try {
|
|
14795
14740
|
const result = await setup(userId);
|
|
14796
|
-
return
|
|
14741
|
+
return jsonResponse14(result);
|
|
14797
14742
|
} catch (err2) {
|
|
14798
|
-
return
|
|
14743
|
+
return jsonResponse14({ error: err2 instanceof Error ? err2.message : "Setup failed" }, 500);
|
|
14799
14744
|
}
|
|
14800
14745
|
}
|
|
14801
14746
|
if (path === "/auth/2fa/enable") {
|
|
14802
|
-
const body = await
|
|
14747
|
+
const body = await parseBody7(request);
|
|
14803
14748
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14804
14749
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14805
|
-
if (!userId || !code2) return
|
|
14750
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14806
14751
|
const result = await enable(userId, code2);
|
|
14807
|
-
return
|
|
14752
|
+
return jsonResponse14(result);
|
|
14808
14753
|
}
|
|
14809
14754
|
if (path === "/auth/2fa/verify") {
|
|
14810
|
-
const body = await
|
|
14755
|
+
const body = await parseBody7(request);
|
|
14811
14756
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14812
14757
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14813
|
-
if (!userId || !code2) return
|
|
14758
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14814
14759
|
const result = await verify(userId, code2);
|
|
14815
|
-
return
|
|
14760
|
+
return jsonResponse14(result);
|
|
14816
14761
|
}
|
|
14817
14762
|
if (path === "/auth/2fa/disable") {
|
|
14818
|
-
const body = await
|
|
14763
|
+
const body = await parseBody7(request);
|
|
14819
14764
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14820
14765
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14821
|
-
if (!userId || !code2) return
|
|
14766
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14822
14767
|
const result = await disable(userId, code2);
|
|
14823
|
-
return
|
|
14768
|
+
return jsonResponse14(result);
|
|
14824
14769
|
}
|
|
14825
14770
|
if (path === "/auth/2fa/backup-codes") {
|
|
14826
|
-
const body = await
|
|
14771
|
+
const body = await parseBody7(request);
|
|
14827
14772
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14828
14773
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14829
|
-
if (!userId || !code2) return
|
|
14774
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14830
14775
|
try {
|
|
14831
14776
|
const result = await regenerateBackupCodes(userId, code2);
|
|
14832
|
-
return
|
|
14777
|
+
return jsonResponse14(result);
|
|
14833
14778
|
} catch (err2) {
|
|
14834
|
-
return
|
|
14779
|
+
return jsonResponse14(
|
|
14835
14780
|
{ error: err2 instanceof Error ? err2.message : "Failed to regenerate codes" },
|
|
14836
14781
|
400
|
|
14837
14782
|
);
|
|
@@ -14851,13 +14796,13 @@ function createTotpModule(config, db) {
|
|
|
14851
14796
|
}
|
|
14852
14797
|
|
|
14853
14798
|
// src/auth/totp-plugin.ts
|
|
14854
|
-
function
|
|
14799
|
+
function jsonResponse15(body, status = 200) {
|
|
14855
14800
|
return new Response(JSON.stringify(body), {
|
|
14856
14801
|
status,
|
|
14857
14802
|
headers: { "Content-Type": "application/json" }
|
|
14858
14803
|
});
|
|
14859
14804
|
}
|
|
14860
|
-
async function
|
|
14805
|
+
async function parseBody8(request) {
|
|
14861
14806
|
try {
|
|
14862
14807
|
return await request.json();
|
|
14863
14808
|
} catch {
|
|
@@ -14879,13 +14824,13 @@ function twoFactor(config) {
|
|
|
14879
14824
|
async handler(request, endpointCtx) {
|
|
14880
14825
|
const user = await endpointCtx.getUser(request);
|
|
14881
14826
|
if (!user) {
|
|
14882
|
-
return
|
|
14827
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14883
14828
|
}
|
|
14884
14829
|
try {
|
|
14885
14830
|
const setup = await module.setup(user.id);
|
|
14886
|
-
return
|
|
14831
|
+
return jsonResponse15(setup);
|
|
14887
14832
|
} catch (err2) {
|
|
14888
|
-
return
|
|
14833
|
+
return jsonResponse15(
|
|
14889
14834
|
{ error: err2 instanceof Error ? err2.message : "Enrollment failed" },
|
|
14890
14835
|
500
|
|
14891
14836
|
);
|
|
@@ -14902,23 +14847,23 @@ function twoFactor(config) {
|
|
|
14902
14847
|
async handler(request, endpointCtx) {
|
|
14903
14848
|
const user = await endpointCtx.getUser(request);
|
|
14904
14849
|
if (!user) {
|
|
14905
|
-
return
|
|
14850
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14906
14851
|
}
|
|
14907
|
-
const body = await
|
|
14852
|
+
const body = await parseBody8(request);
|
|
14908
14853
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14909
14854
|
if (!code2) {
|
|
14910
|
-
return
|
|
14855
|
+
return jsonResponse15({ error: "Missing required field: code" }, 400);
|
|
14911
14856
|
}
|
|
14912
14857
|
const enabled = await module.isEnabled(user.id);
|
|
14913
14858
|
if (!enabled) {
|
|
14914
14859
|
const result2 = await module.enable(user.id, code2);
|
|
14915
14860
|
if (!result2.enabled) {
|
|
14916
|
-
return
|
|
14861
|
+
return jsonResponse15({ error: "Invalid TOTP code" }, 400);
|
|
14917
14862
|
}
|
|
14918
|
-
return
|
|
14863
|
+
return jsonResponse15({ valid: true, activated: true });
|
|
14919
14864
|
}
|
|
14920
14865
|
const result = await module.verify(user.id, code2);
|
|
14921
|
-
return
|
|
14866
|
+
return jsonResponse15(result);
|
|
14922
14867
|
}
|
|
14923
14868
|
});
|
|
14924
14869
|
ctx.addEndpoint({
|
|
@@ -14931,18 +14876,18 @@ function twoFactor(config) {
|
|
|
14931
14876
|
async handler(request, endpointCtx) {
|
|
14932
14877
|
const user = await endpointCtx.getUser(request);
|
|
14933
14878
|
if (!user) {
|
|
14934
|
-
return
|
|
14879
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14935
14880
|
}
|
|
14936
|
-
const body = await
|
|
14881
|
+
const body = await parseBody8(request);
|
|
14937
14882
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14938
14883
|
if (!code2) {
|
|
14939
|
-
return
|
|
14884
|
+
return jsonResponse15({ error: "Missing required field: code" }, 400);
|
|
14940
14885
|
}
|
|
14941
14886
|
const result = await module.disable(user.id, code2);
|
|
14942
14887
|
if (!result.disabled) {
|
|
14943
|
-
return
|
|
14888
|
+
return jsonResponse15({ error: "Invalid TOTP code" }, 400);
|
|
14944
14889
|
}
|
|
14945
|
-
return
|
|
14890
|
+
return jsonResponse15(result);
|
|
14946
14891
|
}
|
|
14947
14892
|
});
|
|
14948
14893
|
ctx.addEndpoint({
|
|
@@ -14955,10 +14900,10 @@ function twoFactor(config) {
|
|
|
14955
14900
|
async handler(request, endpointCtx) {
|
|
14956
14901
|
const user = await endpointCtx.getUser(request);
|
|
14957
14902
|
if (!user) {
|
|
14958
|
-
return
|
|
14903
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14959
14904
|
}
|
|
14960
14905
|
const enabled = await module.isEnabled(user.id);
|
|
14961
|
-
return
|
|
14906
|
+
return jsonResponse15({ enabled });
|
|
14962
14907
|
}
|
|
14963
14908
|
});
|
|
14964
14909
|
ctx.addEndpoint({
|
|
@@ -14971,18 +14916,18 @@ function twoFactor(config) {
|
|
|
14971
14916
|
async handler(request, endpointCtx) {
|
|
14972
14917
|
const user = await endpointCtx.getUser(request);
|
|
14973
14918
|
if (!user) {
|
|
14974
|
-
return
|
|
14919
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14975
14920
|
}
|
|
14976
|
-
const body = await
|
|
14921
|
+
const body = await parseBody8(request);
|
|
14977
14922
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14978
14923
|
if (!code2) {
|
|
14979
|
-
return
|
|
14924
|
+
return jsonResponse15({ error: "Missing required field: code" }, 400);
|
|
14980
14925
|
}
|
|
14981
14926
|
try {
|
|
14982
14927
|
const result = await module.regenerateBackupCodes(user.id, code2);
|
|
14983
|
-
return
|
|
14928
|
+
return jsonResponse15(result);
|
|
14984
14929
|
} catch (err2) {
|
|
14985
|
-
return
|
|
14930
|
+
return jsonResponse15(
|
|
14986
14931
|
{ error: err2 instanceof Error ? err2.message : "Failed to regenerate backup codes" },
|
|
14987
14932
|
400
|
|
14988
14933
|
);
|
|
@@ -15101,7 +15046,7 @@ async function hashPassword(password) {
|
|
|
15101
15046
|
async function verifyPassword(stored, candidate) {
|
|
15102
15047
|
return pbkdf2Verify(candidate, stored);
|
|
15103
15048
|
}
|
|
15104
|
-
function
|
|
15049
|
+
function jsonResponse16(body, status = 200) {
|
|
15105
15050
|
return new Response(JSON.stringify(body), {
|
|
15106
15051
|
status,
|
|
15107
15052
|
headers: { "Content-Type": "application/json" }
|
|
@@ -15225,12 +15170,12 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15225
15170
|
try {
|
|
15226
15171
|
body = await request.json();
|
|
15227
15172
|
} catch {
|
|
15228
|
-
return
|
|
15173
|
+
return jsonResponse16({ error: "Invalid JSON body" }, 400);
|
|
15229
15174
|
}
|
|
15230
15175
|
const b = body;
|
|
15231
15176
|
if (pathname === "/auth/username/sign-up") {
|
|
15232
15177
|
if (typeof b.username !== "string" || typeof b.password !== "string") {
|
|
15233
|
-
return
|
|
15178
|
+
return jsonResponse16({ error: "Missing required fields: username, password" }, 400);
|
|
15234
15179
|
}
|
|
15235
15180
|
try {
|
|
15236
15181
|
const result = await signUp({
|
|
@@ -15238,21 +15183,21 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15238
15183
|
password: b.password,
|
|
15239
15184
|
name: typeof b.name === "string" ? b.name : void 0
|
|
15240
15185
|
});
|
|
15241
|
-
return
|
|
15186
|
+
return jsonResponse16(result, 201);
|
|
15242
15187
|
} catch (err2) {
|
|
15243
|
-
return
|
|
15188
|
+
return jsonResponse16({ error: err2 instanceof Error ? err2.message : "Sign-up failed" }, 400);
|
|
15244
15189
|
}
|
|
15245
15190
|
}
|
|
15246
15191
|
if (pathname === "/auth/username/sign-in") {
|
|
15247
15192
|
if (typeof b.username !== "string" || typeof b.password !== "string") {
|
|
15248
|
-
return
|
|
15193
|
+
return jsonResponse16({ error: "Missing required fields: username, password" }, 400);
|
|
15249
15194
|
}
|
|
15250
15195
|
try {
|
|
15251
15196
|
const result = await signIn({ username: b.username, password: b.password });
|
|
15252
|
-
return
|
|
15197
|
+
return jsonResponse16(result);
|
|
15253
15198
|
} catch (err2) {
|
|
15254
15199
|
if (err2 instanceof Error && err2.message === "Password reset required") {
|
|
15255
|
-
return
|
|
15200
|
+
return jsonResponse16(
|
|
15256
15201
|
{
|
|
15257
15202
|
error: {
|
|
15258
15203
|
code: "PASSWORD_RESET_REQUIRED",
|
|
@@ -15262,21 +15207,21 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15262
15207
|
403
|
|
15263
15208
|
);
|
|
15264
15209
|
}
|
|
15265
|
-
return
|
|
15210
|
+
return jsonResponse16({ error: "Invalid username or password" }, 401);
|
|
15266
15211
|
}
|
|
15267
15212
|
}
|
|
15268
15213
|
if (pathname === "/auth/username/change-password") {
|
|
15269
15214
|
if (typeof b.userId !== "string" || typeof b.current !== "string" || typeof b.newPassword !== "string") {
|
|
15270
|
-
return
|
|
15215
|
+
return jsonResponse16(
|
|
15271
15216
|
{ error: "Missing required fields: userId, current, newPassword" },
|
|
15272
15217
|
400
|
|
15273
15218
|
);
|
|
15274
15219
|
}
|
|
15275
15220
|
try {
|
|
15276
15221
|
const result = await changePassword(b.userId, b.current, b.newPassword);
|
|
15277
|
-
return
|
|
15222
|
+
return jsonResponse16(result);
|
|
15278
15223
|
} catch (err2) {
|
|
15279
|
-
return
|
|
15224
|
+
return jsonResponse16(
|
|
15280
15225
|
{ error: err2 instanceof Error ? err2.message : "Change password failed" },
|
|
15281
15226
|
400
|
|
15282
15227
|
);
|
|
@@ -15284,13 +15229,13 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15284
15229
|
}
|
|
15285
15230
|
if (pathname === "/auth/username/change-username") {
|
|
15286
15231
|
if (typeof b.userId !== "string" || typeof b.newUsername !== "string") {
|
|
15287
|
-
return
|
|
15232
|
+
return jsonResponse16({ error: "Missing required fields: userId, newUsername" }, 400);
|
|
15288
15233
|
}
|
|
15289
15234
|
try {
|
|
15290
15235
|
const result = await changeUsername(b.userId, b.newUsername);
|
|
15291
|
-
return
|
|
15236
|
+
return jsonResponse16(result);
|
|
15292
15237
|
} catch (err2) {
|
|
15293
|
-
return
|
|
15238
|
+
return jsonResponse16(
|
|
15294
15239
|
{ error: err2 instanceof Error ? err2.message : "Change username failed" },
|
|
15295
15240
|
400
|
|
15296
15241
|
);
|
|
@@ -15472,7 +15417,7 @@ function buildStatements(provider) {
|
|
|
15472
15417
|
const isMysql = provider === "mysql";
|
|
15473
15418
|
const ts = isPostgres ? "TIMESTAMPTZ" : isMysql ? "DATETIME(3)" : "INTEGER";
|
|
15474
15419
|
const tsNull = ts;
|
|
15475
|
-
const
|
|
15420
|
+
const json2 = isPostgres ? "JSONB" : isMysql ? "JSON" : "TEXT";
|
|
15476
15421
|
const bool = isPostgres ? "BOOLEAN" : isMysql ? "TINYINT(1)" : "INTEGER";
|
|
15477
15422
|
const ifne = "IF NOT EXISTS";
|
|
15478
15423
|
return [
|
|
@@ -15486,7 +15431,7 @@ function buildStatements(provider) {
|
|
|
15486
15431
|
name TEXT,
|
|
15487
15432
|
external_id TEXT,
|
|
15488
15433
|
external_provider TEXT,
|
|
15489
|
-
metadata ${
|
|
15434
|
+
metadata ${json2},
|
|
15490
15435
|
banned ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15491
15436
|
ban_reason TEXT,
|
|
15492
15437
|
ban_expires_at ${tsNull},
|
|
@@ -15514,7 +15459,7 @@ function buildStatements(provider) {
|
|
|
15514
15459
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15515
15460
|
name TEXT NOT NULL,
|
|
15516
15461
|
slug TEXT NOT NULL UNIQUE,
|
|
15517
|
-
settings ${
|
|
15462
|
+
settings ${json2},
|
|
15518
15463
|
status TEXT NOT NULL DEFAULT 'active',
|
|
15519
15464
|
created_at ${ts} NOT NULL,
|
|
15520
15465
|
updated_at ${ts} NOT NULL
|
|
@@ -15533,7 +15478,7 @@ function buildStatements(provider) {
|
|
|
15533
15478
|
token_prefix TEXT NOT NULL,
|
|
15534
15479
|
expires_at ${tsNull},
|
|
15535
15480
|
last_active_at ${tsNull},
|
|
15536
|
-
metadata ${
|
|
15481
|
+
metadata ${json2},
|
|
15537
15482
|
created_at ${ts} NOT NULL,
|
|
15538
15483
|
updated_at ${ts} NOT NULL
|
|
15539
15484
|
)`,
|
|
@@ -15544,8 +15489,8 @@ function buildStatements(provider) {
|
|
|
15544
15489
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15545
15490
|
agent_id TEXT NOT NULL REFERENCES kavach_agents(id) ON DELETE CASCADE,
|
|
15546
15491
|
resource TEXT NOT NULL,
|
|
15547
|
-
actions ${
|
|
15548
|
-
constraints ${
|
|
15492
|
+
actions ${json2} NOT NULL,
|
|
15493
|
+
constraints ${json2},
|
|
15549
15494
|
created_at ${ts} NOT NULL
|
|
15550
15495
|
)`,
|
|
15551
15496
|
// ------------------------------------------------------------------
|
|
@@ -15555,7 +15500,7 @@ function buildStatements(provider) {
|
|
|
15555
15500
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15556
15501
|
from_agent_id TEXT NOT NULL REFERENCES kavach_agents(id),
|
|
15557
15502
|
to_agent_id TEXT NOT NULL REFERENCES kavach_agents(id),
|
|
15558
|
-
permissions ${
|
|
15503
|
+
permissions ${json2} NOT NULL,
|
|
15559
15504
|
depth INTEGER NOT NULL DEFAULT 1,
|
|
15560
15505
|
max_depth INTEGER NOT NULL DEFAULT 3,
|
|
15561
15506
|
status TEXT NOT NULL DEFAULT 'active',
|
|
@@ -15571,7 +15516,7 @@ function buildStatements(provider) {
|
|
|
15571
15516
|
user_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15572
15517
|
action TEXT NOT NULL,
|
|
15573
15518
|
resource TEXT NOT NULL,
|
|
15574
|
-
parameters ${
|
|
15519
|
+
parameters ${json2},
|
|
15575
15520
|
result TEXT NOT NULL,
|
|
15576
15521
|
reason TEXT,
|
|
15577
15522
|
duration_ms INTEGER NOT NULL,
|
|
@@ -15597,7 +15542,7 @@ function buildStatements(provider) {
|
|
|
15597
15542
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15598
15543
|
name TEXT NOT NULL,
|
|
15599
15544
|
endpoint TEXT NOT NULL UNIQUE,
|
|
15600
|
-
tools ${
|
|
15545
|
+
tools ${json2} NOT NULL,
|
|
15601
15546
|
auth_required ${bool} NOT NULL DEFAULT ${isPostgres ? "TRUE" : "1"},
|
|
15602
15547
|
rate_limit_rpm INTEGER,
|
|
15603
15548
|
status TEXT NOT NULL DEFAULT 'active',
|
|
@@ -15611,7 +15556,7 @@ function buildStatements(provider) {
|
|
|
15611
15556
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15612
15557
|
user_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15613
15558
|
expires_at ${ts} NOT NULL,
|
|
15614
|
-
metadata ${
|
|
15559
|
+
metadata ${json2},
|
|
15615
15560
|
created_at ${ts} NOT NULL
|
|
15616
15561
|
)`,
|
|
15617
15562
|
// ------------------------------------------------------------------
|
|
@@ -15623,13 +15568,13 @@ function buildStatements(provider) {
|
|
|
15623
15568
|
client_secret TEXT,
|
|
15624
15569
|
client_name TEXT,
|
|
15625
15570
|
client_uri TEXT,
|
|
15626
|
-
redirect_uris ${
|
|
15627
|
-
grant_types ${
|
|
15628
|
-
response_types ${
|
|
15571
|
+
redirect_uris ${json2} NOT NULL,
|
|
15572
|
+
grant_types ${json2} NOT NULL,
|
|
15573
|
+
response_types ${json2} NOT NULL,
|
|
15629
15574
|
token_endpoint_auth_method TEXT NOT NULL DEFAULT 'client_secret_basic',
|
|
15630
15575
|
type TEXT NOT NULL DEFAULT 'confidential',
|
|
15631
15576
|
disabled ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15632
|
-
metadata ${
|
|
15577
|
+
metadata ${json2},
|
|
15633
15578
|
created_at ${ts} NOT NULL,
|
|
15634
15579
|
updated_at ${ts} NOT NULL
|
|
15635
15580
|
)`,
|
|
@@ -15697,8 +15642,8 @@ function buildStatements(provider) {
|
|
|
15697
15642
|
agent_id TEXT REFERENCES kavach_agents(id) ON DELETE CASCADE,
|
|
15698
15643
|
user_id TEXT REFERENCES kavach_users(id),
|
|
15699
15644
|
tenant_id TEXT REFERENCES kavach_tenants(id),
|
|
15700
|
-
limits ${
|
|
15701
|
-
current_usage ${
|
|
15645
|
+
limits ${json2} NOT NULL,
|
|
15646
|
+
current_usage ${json2} NOT NULL,
|
|
15702
15647
|
action TEXT NOT NULL DEFAULT 'warn',
|
|
15703
15648
|
status TEXT NOT NULL DEFAULT 'active',
|
|
15704
15649
|
created_at ${ts} NOT NULL
|
|
@@ -15712,11 +15657,11 @@ function buildStatements(provider) {
|
|
|
15712
15657
|
name TEXT NOT NULL,
|
|
15713
15658
|
description TEXT,
|
|
15714
15659
|
version TEXT NOT NULL,
|
|
15715
|
-
protocols ${
|
|
15716
|
-
capabilities ${
|
|
15717
|
-
auth_requirements ${
|
|
15660
|
+
protocols ${json2} NOT NULL,
|
|
15661
|
+
capabilities ${json2} NOT NULL,
|
|
15662
|
+
auth_requirements ${json2} NOT NULL,
|
|
15718
15663
|
endpoint TEXT,
|
|
15719
|
-
metadata ${
|
|
15664
|
+
metadata ${json2},
|
|
15720
15665
|
created_at ${ts} NOT NULL,
|
|
15721
15666
|
updated_at ${ts} NOT NULL
|
|
15722
15667
|
)`,
|
|
@@ -15729,7 +15674,7 @@ function buildStatements(provider) {
|
|
|
15729
15674
|
user_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15730
15675
|
action TEXT NOT NULL,
|
|
15731
15676
|
resource TEXT NOT NULL,
|
|
15732
|
-
arguments ${
|
|
15677
|
+
arguments ${json2},
|
|
15733
15678
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
15734
15679
|
expires_at ${ts} NOT NULL,
|
|
15735
15680
|
responded_at ${tsNull},
|
|
@@ -15743,7 +15688,7 @@ function buildStatements(provider) {
|
|
|
15743
15688
|
agent_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_agents(id) ON DELETE CASCADE,
|
|
15744
15689
|
score INTEGER NOT NULL,
|
|
15745
15690
|
level TEXT NOT NULL,
|
|
15746
|
-
factors ${
|
|
15691
|
+
factors ${json2} NOT NULL,
|
|
15747
15692
|
computed_at ${ts} NOT NULL
|
|
15748
15693
|
)`,
|
|
15749
15694
|
// ------------------------------------------------------------------
|
|
@@ -15754,7 +15699,7 @@ function buildStatements(provider) {
|
|
|
15754
15699
|
name TEXT NOT NULL,
|
|
15755
15700
|
slug TEXT NOT NULL UNIQUE,
|
|
15756
15701
|
owner_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15757
|
-
metadata ${
|
|
15702
|
+
metadata ${json2},
|
|
15758
15703
|
created_at ${ts} NOT NULL,
|
|
15759
15704
|
updated_at ${ts} NOT NULL
|
|
15760
15705
|
)`,
|
|
@@ -15789,7 +15734,7 @@ function buildStatements(provider) {
|
|
|
15789
15734
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15790
15735
|
org_id TEXT NOT NULL REFERENCES kavach_organizations(id) ON DELETE CASCADE,
|
|
15791
15736
|
name TEXT NOT NULL,
|
|
15792
|
-
permissions ${
|
|
15737
|
+
permissions ${json2} NOT NULL,
|
|
15793
15738
|
UNIQUE(org_id, name)
|
|
15794
15739
|
)`,
|
|
15795
15740
|
// ------------------------------------------------------------------
|
|
@@ -15825,7 +15770,7 @@ function buildStatements(provider) {
|
|
|
15825
15770
|
token_hash TEXT NOT NULL UNIQUE,
|
|
15826
15771
|
purpose TEXT NOT NULL,
|
|
15827
15772
|
identifier TEXT NOT NULL,
|
|
15828
|
-
metadata ${
|
|
15773
|
+
metadata ${json2},
|
|
15829
15774
|
used ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15830
15775
|
expires_at ${ts} NOT NULL,
|
|
15831
15776
|
created_at ${ts} NOT NULL
|
|
@@ -15870,7 +15815,7 @@ function buildStatements(provider) {
|
|
|
15870
15815
|
user_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_users(id),
|
|
15871
15816
|
secret TEXT NOT NULL,
|
|
15872
15817
|
enabled ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15873
|
-
backup_codes ${
|
|
15818
|
+
backup_codes ${json2} NOT NULL,
|
|
15874
15819
|
created_at ${ts} NOT NULL,
|
|
15875
15820
|
updated_at ${ts} NOT NULL
|
|
15876
15821
|
)`,
|
|
@@ -15895,7 +15840,7 @@ function buildStatements(provider) {
|
|
|
15895
15840
|
name TEXT NOT NULL,
|
|
15896
15841
|
key_hash TEXT NOT NULL,
|
|
15897
15842
|
key_prefix TEXT NOT NULL,
|
|
15898
|
-
permissions ${
|
|
15843
|
+
permissions ${json2} NOT NULL,
|
|
15899
15844
|
expires_at ${tsNull},
|
|
15900
15845
|
last_used_at ${tsNull},
|
|
15901
15846
|
created_at ${ts} NOT NULL
|
|
@@ -15954,10 +15899,10 @@ function buildStatements(provider) {
|
|
|
15954
15899
|
client_id TEXT NOT NULL UNIQUE,
|
|
15955
15900
|
client_secret_hash TEXT NOT NULL,
|
|
15956
15901
|
client_name TEXT NOT NULL,
|
|
15957
|
-
redirect_uris ${
|
|
15958
|
-
grant_types ${
|
|
15959
|
-
response_types ${
|
|
15960
|
-
scopes ${
|
|
15902
|
+
redirect_uris ${json2} NOT NULL,
|
|
15903
|
+
grant_types ${json2} NOT NULL,
|
|
15904
|
+
response_types ${json2} NOT NULL,
|
|
15905
|
+
scopes ${json2} NOT NULL,
|
|
15961
15906
|
token_endpoint_auth_method TEXT NOT NULL DEFAULT 'client_secret_post',
|
|
15962
15907
|
created_at ${ts} NOT NULL,
|
|
15963
15908
|
updated_at ${ts} NOT NULL
|
|
@@ -16003,7 +15948,7 @@ function buildStatements(provider) {
|
|
|
16003
15948
|
output_tokens INTEGER,
|
|
16004
15949
|
cost_micros INTEGER NOT NULL,
|
|
16005
15950
|
currency TEXT NOT NULL DEFAULT 'USD',
|
|
16006
|
-
metadata ${
|
|
15951
|
+
metadata ${json2},
|
|
16007
15952
|
delegation_chain_id TEXT,
|
|
16008
15953
|
recorded_at ${ts} NOT NULL
|
|
16009
15954
|
)`,
|
|
@@ -16051,7 +15996,7 @@ function buildStatements(provider) {
|
|
|
16051
15996
|
id TEXT NOT NULL PRIMARY KEY,
|
|
16052
15997
|
type TEXT NOT NULL,
|
|
16053
15998
|
timestamp ${ts} NOT NULL,
|
|
16054
|
-
data ${
|
|
15999
|
+
data ${json2} NOT NULL,
|
|
16055
16000
|
agent_id TEXT,
|
|
16056
16001
|
user_id TEXT
|
|
16057
16002
|
)`,
|
|
@@ -16112,7 +16057,7 @@ function buildStatements(provider) {
|
|
|
16112
16057
|
source_instance_id TEXT NOT NULL,
|
|
16113
16058
|
target_instance_id TEXT,
|
|
16114
16059
|
direction TEXT NOT NULL,
|
|
16115
|
-
permissions ${
|
|
16060
|
+
permissions ${json2} NOT NULL,
|
|
16116
16061
|
trust_score INTEGER,
|
|
16117
16062
|
expires_at ${ts} NOT NULL,
|
|
16118
16063
|
created_at ${ts} NOT NULL
|
|
@@ -17430,6 +17375,18 @@ function matchPath(pattern, pathname) {
|
|
|
17430
17375
|
return params;
|
|
17431
17376
|
}
|
|
17432
17377
|
function createPluginRouter(endpoints) {
|
|
17378
|
+
const rateLimitStore = /* @__PURE__ */ new Map();
|
|
17379
|
+
function checkRateLimit2(key, windowSeconds, max) {
|
|
17380
|
+
const now = Date.now();
|
|
17381
|
+
const entry = rateLimitStore.get(key);
|
|
17382
|
+
if (!entry || now > entry.resetAt) {
|
|
17383
|
+
rateLimitStore.set(key, { count: 1, resetAt: now + windowSeconds * 1e3 });
|
|
17384
|
+
return true;
|
|
17385
|
+
}
|
|
17386
|
+
if (entry.count >= max) return false;
|
|
17387
|
+
entry.count++;
|
|
17388
|
+
return true;
|
|
17389
|
+
}
|
|
17433
17390
|
return {
|
|
17434
17391
|
async handle(request, basePath, endpointCtx) {
|
|
17435
17392
|
const url = new URL(request.url);
|
|
@@ -17449,6 +17406,20 @@ function createPluginRouter(endpoints) {
|
|
|
17449
17406
|
if (endpoint.method !== method) continue;
|
|
17450
17407
|
const params = matchPath(endpoint.path, pathname);
|
|
17451
17408
|
if (params === null) continue;
|
|
17409
|
+
if (endpoint.metadata?.rateLimit) {
|
|
17410
|
+
const { window: windowSec, max } = endpoint.metadata.rateLimit;
|
|
17411
|
+
const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? "unknown";
|
|
17412
|
+
const key = `${ip}:${endpoint.path}`;
|
|
17413
|
+
if (!checkRateLimit2(key, windowSec, max)) {
|
|
17414
|
+
return json({ error: "Rate limit exceeded" }, 429);
|
|
17415
|
+
}
|
|
17416
|
+
}
|
|
17417
|
+
if (endpoint.metadata?.requireAuth) {
|
|
17418
|
+
const user = await endpointCtx.getUser(request);
|
|
17419
|
+
if (!user) {
|
|
17420
|
+
return json({ error: "Authentication required" }, 401);
|
|
17421
|
+
}
|
|
17422
|
+
}
|
|
17452
17423
|
const enrichedUrl = new URL(request.url);
|
|
17453
17424
|
for (const [key, value] of Object.entries(params)) {
|
|
17454
17425
|
enrichedUrl.searchParams.set(`_param_${key}`, value);
|
|
@@ -17506,7 +17477,7 @@ async function runMigrations(db, provider, statements) {
|
|
|
17506
17477
|
}
|
|
17507
17478
|
throw new Error(`runMigrations: unsupported provider "${provider}"`);
|
|
17508
17479
|
}
|
|
17509
|
-
async function initializePlugins(plugins, db, config) {
|
|
17480
|
+
async function initializePlugins(plugins, db, config, sessionManager) {
|
|
17510
17481
|
const registry = {
|
|
17511
17482
|
endpoints: [],
|
|
17512
17483
|
migrations: [],
|
|
@@ -17523,6 +17494,7 @@ async function initializePlugins(plugins, db, config) {
|
|
|
17523
17494
|
const ctx = {
|
|
17524
17495
|
db,
|
|
17525
17496
|
config,
|
|
17497
|
+
sessionManager,
|
|
17526
17498
|
addEndpoint(endpoint) {
|
|
17527
17499
|
registry.endpoints.push(endpoint);
|
|
17528
17500
|
},
|
|
@@ -17763,8 +17735,8 @@ var DEFAULT_EXCLUDE_PATHS = [
|
|
|
17763
17735
|
var encoder = new TextEncoder();
|
|
17764
17736
|
var decoder = new TextDecoder();
|
|
17765
17737
|
function encodeState2(state) {
|
|
17766
|
-
const
|
|
17767
|
-
const bytes = encoder.encode(
|
|
17738
|
+
const json2 = JSON.stringify(state);
|
|
17739
|
+
const bytes = encoder.encode(json2);
|
|
17768
17740
|
let binary = "";
|
|
17769
17741
|
for (let i = 0; i < bytes.length; i++) {
|
|
17770
17742
|
binary += String.fromCharCode(bytes[i]);
|
|
@@ -17782,8 +17754,8 @@ function decodeState(encoded) {
|
|
|
17782
17754
|
for (let i = 0; i < binary.length; i++) {
|
|
17783
17755
|
bytes[i] = binary.charCodeAt(i);
|
|
17784
17756
|
}
|
|
17785
|
-
const
|
|
17786
|
-
const parsed = JSON.parse(
|
|
17757
|
+
const json2 = decoder.decode(bytes);
|
|
17758
|
+
const parsed = JSON.parse(json2);
|
|
17787
17759
|
if (!isValidChainState(parsed)) return null;
|
|
17788
17760
|
return parsed;
|
|
17789
17761
|
} catch {
|
|
@@ -17855,7 +17827,7 @@ function createRedirectChain(config) {
|
|
|
17855
17827
|
if (cookieOpts.domain) parts.push(`Domain=${cookieOpts.domain}`);
|
|
17856
17828
|
return parts.join("; ");
|
|
17857
17829
|
}
|
|
17858
|
-
function
|
|
17830
|
+
function buildClearCookie2() {
|
|
17859
17831
|
return buildCookieHeader("", 0);
|
|
17860
17832
|
}
|
|
17861
17833
|
function serializeAndSetCookie(state) {
|
|
@@ -17965,7 +17937,7 @@ function createRedirectChain(config) {
|
|
|
17965
17937
|
pop(request) {
|
|
17966
17938
|
const state = parseFromRequest(request);
|
|
17967
17939
|
if (!state) {
|
|
17968
|
-
return { url: defaultPath, done: true, clearCookie:
|
|
17940
|
+
return { url: defaultPath, done: true, clearCookie: buildClearCookie2() };
|
|
17969
17941
|
}
|
|
17970
17942
|
if (state.steps.length > 0) {
|
|
17971
17943
|
const next = state.steps.shift();
|
|
@@ -17989,7 +17961,7 @@ function createRedirectChain(config) {
|
|
|
17989
17961
|
return {
|
|
17990
17962
|
url,
|
|
17991
17963
|
done: true,
|
|
17992
|
-
clearCookie:
|
|
17964
|
+
clearCookie: buildClearCookie2()
|
|
17993
17965
|
};
|
|
17994
17966
|
},
|
|
17995
17967
|
peek(request) {
|
|
@@ -18015,7 +17987,7 @@ function createRedirectChain(config) {
|
|
|
18015
17987
|
},
|
|
18016
17988
|
clear() {
|
|
18017
17989
|
currentState = null;
|
|
18018
|
-
return
|
|
17990
|
+
return buildClearCookie2();
|
|
18019
17991
|
},
|
|
18020
17992
|
parse(request) {
|
|
18021
17993
|
return parseFromRequest(request);
|
|
@@ -18348,12 +18320,33 @@ async function createKavach(config) {
|
|
|
18348
18320
|
const captchaModule = config.captcha ? createCaptchaModule(config.captcha) : null;
|
|
18349
18321
|
const redirectChain = createRedirectChain(config.redirects);
|
|
18350
18322
|
const webhookModule = config.webhooks && config.webhooks.length > 0 ? createWebhookModule(config.webhooks) : null;
|
|
18351
|
-
const pluginRegistry = await initializePlugins(config.plugins ?? [], db, config);
|
|
18323
|
+
const pluginRegistry = await initializePlugins(config.plugins ?? [], db, config, sessionManager);
|
|
18352
18324
|
const endpointCtx = {
|
|
18353
18325
|
db,
|
|
18354
18326
|
async getUser(request) {
|
|
18355
|
-
if (
|
|
18356
|
-
|
|
18327
|
+
if (authAdapter) {
|
|
18328
|
+
const user = await authAdapter.resolveUser(request);
|
|
18329
|
+
if (user) return user;
|
|
18330
|
+
}
|
|
18331
|
+
if (sessionManager) {
|
|
18332
|
+
const token = extractToken(request);
|
|
18333
|
+
if (!token) return null;
|
|
18334
|
+
try {
|
|
18335
|
+
const session = await sessionManager.validate(token);
|
|
18336
|
+
if (!session) return null;
|
|
18337
|
+
const userRows = await db.select().from(users).where(eq(users.id, session.userId));
|
|
18338
|
+
const user = userRows[0];
|
|
18339
|
+
if (!user) return null;
|
|
18340
|
+
return {
|
|
18341
|
+
id: user.id,
|
|
18342
|
+
email: user.email ?? void 0,
|
|
18343
|
+
name: user.name ?? void 0
|
|
18344
|
+
};
|
|
18345
|
+
} catch {
|
|
18346
|
+
return null;
|
|
18347
|
+
}
|
|
18348
|
+
}
|
|
18349
|
+
return null;
|
|
18357
18350
|
},
|
|
18358
18351
|
async getSession(token) {
|
|
18359
18352
|
if (!sessionManager) return null;
|
|
@@ -18361,6 +18354,46 @@ async function createKavach(config) {
|
|
|
18361
18354
|
}
|
|
18362
18355
|
};
|
|
18363
18356
|
const pluginRouter = createPluginRouter(pluginRegistry.endpoints);
|
|
18357
|
+
if (sessionManager) {
|
|
18358
|
+
pluginRegistry.endpoints.push({
|
|
18359
|
+
method: "POST",
|
|
18360
|
+
path: "/auth/sign-out",
|
|
18361
|
+
metadata: { description: "Revoke the current session and clear cookie" },
|
|
18362
|
+
async handler(request) {
|
|
18363
|
+
const token = extractToken(request);
|
|
18364
|
+
if (!token) return json({ error: "No session" }, 401);
|
|
18365
|
+
try {
|
|
18366
|
+
const session = await sessionManager.validate(token);
|
|
18367
|
+
if (session) {
|
|
18368
|
+
await sessionManager.revoke(session.id);
|
|
18369
|
+
}
|
|
18370
|
+
} catch {
|
|
18371
|
+
}
|
|
18372
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
18373
|
+
status: 200,
|
|
18374
|
+
headers: {
|
|
18375
|
+
"Content-Type": "application/json",
|
|
18376
|
+
"Set-Cookie": buildClearCookie("kavach_session")
|
|
18377
|
+
}
|
|
18378
|
+
});
|
|
18379
|
+
}
|
|
18380
|
+
});
|
|
18381
|
+
pluginRegistry.endpoints.push({
|
|
18382
|
+
method: "GET",
|
|
18383
|
+
path: "/auth/session",
|
|
18384
|
+
metadata: { description: "Get current session and user info" },
|
|
18385
|
+
async handler(request) {
|
|
18386
|
+
const user = await endpointCtx.getUser(request);
|
|
18387
|
+
if (!user) return json({ error: "Not authenticated" }, 401);
|
|
18388
|
+
const token = extractToken(request);
|
|
18389
|
+
const session = token ? await sessionManager.validate(token) : null;
|
|
18390
|
+
return json({
|
|
18391
|
+
user,
|
|
18392
|
+
session: session ? { id: session.id, expiresAt: session.expiresAt } : null
|
|
18393
|
+
});
|
|
18394
|
+
}
|
|
18395
|
+
});
|
|
18396
|
+
}
|
|
18364
18397
|
async function authorize(agentId, request, context) {
|
|
18365
18398
|
if (hooks.beforeAuthorize) {
|
|
18366
18399
|
const verdict = await hooks.beforeAuthorize({
|
|
@@ -19432,7 +19465,7 @@ function parseCookies2(header) {
|
|
|
19432
19465
|
}
|
|
19433
19466
|
return result;
|
|
19434
19467
|
}
|
|
19435
|
-
function
|
|
19468
|
+
function getCookie2(header, name) {
|
|
19436
19469
|
return parseCookies2(header)[name];
|
|
19437
19470
|
}
|
|
19438
19471
|
function parseCookiesFromRequest(request) {
|
|
@@ -19553,7 +19586,7 @@ function createCookieSessionManager(config, db) {
|
|
|
19553
19586
|
...config.cookieOptions,
|
|
19554
19587
|
maxAge: maxAgeSecs
|
|
19555
19588
|
};
|
|
19556
|
-
function
|
|
19589
|
+
function buildSetCookie2(token) {
|
|
19557
19590
|
return serializeCookie(sessionName, token, baseCookieOpts);
|
|
19558
19591
|
}
|
|
19559
19592
|
function buildDeleteCookie() {
|
|
@@ -19562,10 +19595,10 @@ function createCookieSessionManager(config, db) {
|
|
|
19562
19595
|
}
|
|
19563
19596
|
async function createSession(userId, metadata) {
|
|
19564
19597
|
const { session, token } = await raw.create(userId, metadata);
|
|
19565
|
-
return { session, setCookieHeader:
|
|
19598
|
+
return { session, setCookieHeader: buildSetCookie2(token) };
|
|
19566
19599
|
}
|
|
19567
19600
|
async function validateSession(cookieHeader) {
|
|
19568
|
-
const token =
|
|
19601
|
+
const token = getCookie2(cookieHeader, sessionName);
|
|
19569
19602
|
if (!token) {
|
|
19570
19603
|
return { session: null, refreshCookieHeader: null };
|
|
19571
19604
|
}
|
|
@@ -19591,7 +19624,7 @@ function createCookieSessionManager(config, db) {
|
|
|
19591
19624
|
row.userId,
|
|
19592
19625
|
row.metadata ?? void 0
|
|
19593
19626
|
);
|
|
19594
|
-
return { session: newSession, setCookieHeader:
|
|
19627
|
+
return { session: newSession, setCookieHeader: buildSetCookie2(newToken) };
|
|
19595
19628
|
}
|
|
19596
19629
|
async function revokeSession(sessionId) {
|
|
19597
19630
|
await raw.revoke(sessionId);
|
|
@@ -19935,7 +19968,7 @@ function createSessionRefresher(config) {
|
|
|
19935
19968
|
}
|
|
19936
19969
|
async function handleRequest(request) {
|
|
19937
19970
|
const cookieHeader = request.headers.get("cookie") ?? "";
|
|
19938
|
-
let rawToken =
|
|
19971
|
+
let rawToken = getCookie2(cookieHeader, refreshCookieName);
|
|
19939
19972
|
if (!rawToken) {
|
|
19940
19973
|
try {
|
|
19941
19974
|
const body = await request.clone().json();
|
|
@@ -20666,6 +20699,6 @@ async function verifyWebhookSignature3(secret, rawBody, signature) {
|
|
|
20666
20699
|
return diff === 0;
|
|
20667
20700
|
}
|
|
20668
20701
|
|
|
20669
|
-
export { CredentialStatusSchema, CredentialSubjectSchema, EVENT_TYPES, HibpApiError, HibpBreachedError, KAVACH_AGENT_CREDENTIAL, KAVACH_DELEGATION_CREDENTIAL, KAVACH_PERMISSION_CREDENTIAL, KVStore, MemoryStore, MultiSessionLimitError, OAuthProxyError, OneTapVerifyError, ProofSchema, RefreshTokenError, SSO_ERROR, SsoError, VC_CONTEXT_V1, VC_CONTEXT_V2, VC_TYPE_CREDENTIAL, VC_TYPE_PRESENTATION, VerifiableCredentialSchema, VerifiablePresentationSchema, additionalFields, admin, agentCards, agentDids, agents, anonymousAuth, apiKeys2 as apiKeys, apiKeys as apiKeysTable, approvalRequests, auditLogs, bearerAuth, budgetPolicies, buildDidDocument, buildSessionMetadata, classifyViolation, constantTimeEqual, createAdditionalFieldsModule, createAdminModule, createAgentModule, createAnonymousAuthModule, createApiKeyManagerModule, createAppleProvider, createApprovalModule, createAuditModule, createCaptchaModule, createCookieSessionManager, createCostAttributionModule, createCustomSessionModule, createDatabase, createDatabaseSync, createDelegationModule, createDeviceAuthModule, createDidModule, createDiscordProvider, createEmailOtpModule, createEmailTemplates, createEmailVerificationModule, createEphemeralSessionModule, createEventStreamModule, createFederationModule, createGdprModule, createGithubProvider, createGitlabProvider, createGoogleProvider, createHibpModule, createI18n, createJwtSessionModule, createKavach, createLastLoginModule, createLinkedInProvider, createMagicLinkModule, createMicrosoftProvider, createMultiSessionModule, createOAuthModule, createOAuthProxyModule, createOidcProviderModule, createOneTapModule, createOneTimeTokenModule, createOpenApiModule, createOrgModule, createPasskeyModule, createPasswordResetModule, createPermissionEngine, createPhoneAuthModule, createPluginRouter, createPolarModule, createPolicyModule, createPresentation, createPrivilegeAnalyzer, createRateLimiter, createReBACModule, createRedirectChain, createScimModule, createSessionFreshnessModule, createSessionManager, createSessionRefresher, createSiweModule, createSlackProvider, createSsoModule, createStripeModule, createTables, createTenantModule, createTokenFamilyStore, createTotpModule, createTrustModule, createTrustedDeviceModule, createTwitterProvider, createUsernameAuthModule, createVCIssuer, createVCVerifier, createWebhookModule2 as createWebhookModule, customAuth, customSession, de, delegationChains, deviceAuth, deviceLabelFromRequest, emailOtp, emailOtps, en, es, fr, fromBase64Url, fromHex, gdpr, generateCsrfToken, generateDidKey, generateDidWeb, generateId, generateOpenAPISpec, getCookie, getDidWebUrl, getPermissionTemplate, headerAuth, hmacSha1Raw, hmacSha256, hmacSha256Raw, importHmacKey, initializePlugins, ja, kvStore, magicLink, magicLinks, mcpServers, oauth, oauthAccessTokens, oauthAuthorizationCodes, oauthClients, oauthProxy, oneTap, orgInvitations, orgMembers, orgRoles, organization, organizations, parseCookies2 as parseCookies, parseCookiesFromRequest, passkey, passkeyChallenges, passkeyCredentials, pbkdf2Hash, pbkdf2Verify, permissionTemplates, permissions, polar, randomBytes, randomBytesHex, rateLimit, rateLimits, resolveDidKey, resolveDidWeb, scim, serializeCookie, serializeCookieDeletion, sessions, sha1, sha256, sha256Raw, signPayload2 as signPayload, siwe, ssoConnections, stripe, tenants, toBase64Url, toHex, totpRecords, trustScores, twoFactor, users, validateCsrfToken, validateOrigin, verifyPayload, verifyPresentation, verifyWebhookSignature3 as verifyWebhookSignature, withRateLimit, zh };
|
|
20702
|
+
export { CredentialStatusSchema, CredentialSubjectSchema, EVENT_TYPES, HibpApiError, HibpBreachedError, KAVACH_AGENT_CREDENTIAL, KAVACH_DELEGATION_CREDENTIAL, KAVACH_PERMISSION_CREDENTIAL, KVStore, MemoryStore, MultiSessionLimitError, OAuthProxyError, OneTapVerifyError, ProofSchema, RefreshTokenError, SSO_ERROR, SsoError, VC_CONTEXT_V1, VC_CONTEXT_V2, VC_TYPE_CREDENTIAL, VC_TYPE_PRESENTATION, VerifiableCredentialSchema, VerifiablePresentationSchema, additionalFields, admin, agentCards, agentDids, agents, anonymousAuth, apiKeys2 as apiKeys, apiKeys as apiKeysTable, approvalRequests, auditLogs, bearerAuth, budgetPolicies, buildDidDocument, buildSessionMetadata, classifyViolation, constantTimeEqual, createAdditionalFieldsModule, createAdminModule, createAgentModule, createAnonymousAuthModule, createApiKeyManagerModule, createAppleProvider, createApprovalModule, createAuditModule, createCaptchaModule, createCookieSessionManager, createCostAttributionModule, createCustomSessionModule, createDatabase, createDatabaseSync, createDelegationModule, createDeviceAuthModule, createDidModule, createDiscordProvider, createEmailOtpModule, createEmailTemplates, createEmailVerificationModule, createEphemeralSessionModule, createEventStreamModule, createFederationModule, createGdprModule, createGithubProvider, createGitlabProvider, createGoogleProvider, createHibpModule, createI18n, createJwtSessionModule, createKavach, createLastLoginModule, createLinkedInProvider, createMagicLinkModule, createMicrosoftProvider, createMultiSessionModule, createOAuthModule, createOAuthProxyModule, createOidcProviderModule, createOneTapModule, createOneTimeTokenModule, createOpenApiModule, createOrgModule, createPasskeyModule, createPasswordResetModule, createPermissionEngine, createPhoneAuthModule, createPluginRouter, createPolarModule, createPolicyModule, createPresentation, createPrivilegeAnalyzer, createRateLimiter, createReBACModule, createRedirectChain, createScimModule, createSessionFreshnessModule, createSessionManager, createSessionRefresher, createSiweModule, createSlackProvider, createSsoModule, createStripeModule, createTables, createTenantModule, createTokenFamilyStore, createTotpModule, createTrustModule, createTrustedDeviceModule, createTwitterProvider, createUsernameAuthModule, createVCIssuer, createVCVerifier, createWebhookModule2 as createWebhookModule, customAuth, customSession, de, delegationChains, deviceAuth, deviceLabelFromRequest, emailOtp, emailOtps, en, es, fr, fromBase64Url, fromHex, gdpr, generateCsrfToken, generateDidKey, generateDidWeb, generateId, generateOpenAPISpec, getCookie2 as getCookie, getDidWebUrl, getPermissionTemplate, headerAuth, hmacSha1Raw, hmacSha256, hmacSha256Raw, importHmacKey, initializePlugins, ja, kvStore, magicLink, magicLinks, mcpServers, oauth, oauthAccessTokens, oauthAuthorizationCodes, oauthClients, oauthProxy, oneTap, orgInvitations, orgMembers, orgRoles, organization, organizations, parseCookies2 as parseCookies, parseCookiesFromRequest, passkey, passkeyChallenges, passkeyCredentials, pbkdf2Hash, pbkdf2Verify, permissionTemplates, permissions, polar, randomBytes, randomBytesHex, rateLimit, rateLimits, resolveDidKey, resolveDidWeb, scim, serializeCookie, serializeCookieDeletion, sessions, sha1, sha256, sha256Raw, signPayload2 as signPayload, siwe, ssoConnections, stripe, tenants, toBase64Url, toHex, totpRecords, trustScores, twoFactor, users, validateCsrfToken, validateOrigin, verifyPayload, verifyPresentation, verifyWebhookSignature3 as verifyWebhookSignature, withRateLimit, zh };
|
|
20670
20703
|
//# sourceMappingURL=index.js.map
|
|
20671
20704
|
//# sourceMappingURL=index.js.map
|