kavachos 0.1.4 → 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 +716 -755
- package/dist/auth/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +713 -648
- 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,9 +5660,7 @@ function createOAuthModule(db, config) {
|
|
|
5792
5660
|
pruneExpiredStates
|
|
5793
5661
|
};
|
|
5794
5662
|
}
|
|
5795
|
-
|
|
5796
|
-
// src/auth/oauth/plugin.ts
|
|
5797
|
-
function jsonResponse10(body, status = 200) {
|
|
5663
|
+
function jsonResponse5(body, status = 200) {
|
|
5798
5664
|
return new Response(JSON.stringify(body), {
|
|
5799
5665
|
status,
|
|
5800
5666
|
headers: { "Content-Type": "application/json" }
|
|
@@ -5812,6 +5678,12 @@ function oauth(config) {
|
|
|
5812
5678
|
async init(ctx) {
|
|
5813
5679
|
const module = createOAuthModule(ctx.db, config);
|
|
5814
5680
|
const baseUrl = ctx.config.baseUrl ?? "";
|
|
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,21 +5726,62 @@ 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 {
|
|
5864
5736
|
const result = await module.handleCallback(provider, code2, state, redirectUri);
|
|
5865
|
-
|
|
5737
|
+
const email = result.userInfo.email;
|
|
5738
|
+
let userId = result.account.userId;
|
|
5739
|
+
if (userId === "__pending__" && email && ctx.db) {
|
|
5740
|
+
const existing = await ctx.db.select().from(users).where(eq(users.email, email));
|
|
5741
|
+
if (existing[0]) {
|
|
5742
|
+
userId = existing[0].id;
|
|
5743
|
+
} else {
|
|
5744
|
+
const newId = crypto.randomUUID();
|
|
5745
|
+
await ctx.db.insert(users).values({
|
|
5746
|
+
id: newId,
|
|
5747
|
+
email,
|
|
5748
|
+
name: result.userInfo.name ?? null,
|
|
5749
|
+
externalProvider: `oauth:${provider}`,
|
|
5750
|
+
externalId: result.userInfo.id,
|
|
5751
|
+
emailVerified: 1,
|
|
5752
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
5753
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
5754
|
+
});
|
|
5755
|
+
userId = newId;
|
|
5756
|
+
}
|
|
5757
|
+
await module.linkAccount(userId, provider, result.userInfo, {
|
|
5758
|
+
accessToken: result.account.accessToken,
|
|
5759
|
+
refreshToken: result.account.refreshToken ?? void 0,
|
|
5760
|
+
tokenType: "Bearer",
|
|
5761
|
+
raw: {}
|
|
5762
|
+
});
|
|
5763
|
+
}
|
|
5764
|
+
if (userId !== "__pending__") {
|
|
5765
|
+
const { session, token } = await sessionManager.create(userId);
|
|
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
|
+
});
|
|
5777
|
+
}
|
|
5778
|
+
return jsonResponse5({
|
|
5866
5779
|
isNewAccount: result.isNewAccount,
|
|
5867
5780
|
account: result.account,
|
|
5868
5781
|
userInfo: result.userInfo
|
|
5869
5782
|
});
|
|
5870
5783
|
} catch (err2) {
|
|
5871
|
-
return
|
|
5784
|
+
return jsonResponse5(
|
|
5872
5785
|
{ error: err2 instanceof Error ? err2.message : "OAuth callback failed" },
|
|
5873
5786
|
400
|
|
5874
5787
|
);
|
|
@@ -5885,29 +5798,29 @@ function oauth(config) {
|
|
|
5885
5798
|
async handler(request, endpointCtx) {
|
|
5886
5799
|
const user = await endpointCtx.getUser(request);
|
|
5887
5800
|
if (!user) {
|
|
5888
|
-
return
|
|
5801
|
+
return jsonResponse5({ error: "Authentication required" }, 401);
|
|
5889
5802
|
}
|
|
5890
5803
|
let body;
|
|
5891
5804
|
try {
|
|
5892
5805
|
body = await request.json();
|
|
5893
5806
|
} catch {
|
|
5894
|
-
return
|
|
5807
|
+
return jsonResponse5({ error: "Invalid JSON body" }, 400);
|
|
5895
5808
|
}
|
|
5896
5809
|
const b = body;
|
|
5897
5810
|
const provider = typeof b.provider === "string" ? b.provider : null;
|
|
5898
5811
|
const userInfo = typeof b.userInfo === "object" && b.userInfo !== null ? b.userInfo : null;
|
|
5899
5812
|
const tokens = typeof b.tokens === "object" && b.tokens !== null ? b.tokens : null;
|
|
5900
5813
|
if (!provider || !userInfo || !tokens) {
|
|
5901
|
-
return
|
|
5814
|
+
return jsonResponse5(
|
|
5902
5815
|
{ error: "Missing required fields: provider, userInfo, tokens" },
|
|
5903
5816
|
400
|
|
5904
5817
|
);
|
|
5905
5818
|
}
|
|
5906
5819
|
try {
|
|
5907
5820
|
const account = await module.linkAccount(user.id, provider, userInfo, tokens);
|
|
5908
|
-
return
|
|
5821
|
+
return jsonResponse5({ account });
|
|
5909
5822
|
} catch (err2) {
|
|
5910
|
-
return
|
|
5823
|
+
return jsonResponse5(
|
|
5911
5824
|
{ error: err2 instanceof Error ? err2.message : "Failed to link account" },
|
|
5912
5825
|
400
|
|
5913
5826
|
);
|
|
@@ -5923,7 +5836,7 @@ function oauth(config) {
|
|
|
5923
5836
|
id: p2.id,
|
|
5924
5837
|
name: p2.name
|
|
5925
5838
|
}));
|
|
5926
|
-
return
|
|
5839
|
+
return jsonResponse5({ providers });
|
|
5927
5840
|
}
|
|
5928
5841
|
});
|
|
5929
5842
|
}
|
|
@@ -7720,25 +7633,25 @@ function createOneTapModule(config, db, sessionManager) {
|
|
|
7720
7633
|
const text3 = await request.text();
|
|
7721
7634
|
formData = new URLSearchParams(text3);
|
|
7722
7635
|
} catch {
|
|
7723
|
-
return
|
|
7636
|
+
return jsonResponse6({ error: "Failed to parse request body" }, 400);
|
|
7724
7637
|
}
|
|
7725
7638
|
const credential = formData.get("credential");
|
|
7726
7639
|
const bodyToken = formData.get(csrfCookieName);
|
|
7727
7640
|
if (!credential) {
|
|
7728
|
-
return
|
|
7641
|
+
return jsonResponse6({ error: "Missing credential field" }, 400);
|
|
7729
7642
|
}
|
|
7730
7643
|
const cookieToken = getCsrfCookie(request);
|
|
7731
7644
|
if (!cookieToken || !bodyToken || cookieToken !== bodyToken) {
|
|
7732
|
-
return
|
|
7645
|
+
return jsonResponse6({ error: "CSRF token mismatch" }, 403);
|
|
7733
7646
|
}
|
|
7734
7647
|
let googleUser;
|
|
7735
7648
|
try {
|
|
7736
7649
|
googleUser = await verify(credential);
|
|
7737
7650
|
} catch (err2) {
|
|
7738
7651
|
if (err2 instanceof OneTapVerifyError && err2.code === "USER_NOT_FOUND") {
|
|
7739
|
-
return
|
|
7652
|
+
return jsonResponse6({ error: err2.message }, 403);
|
|
7740
7653
|
}
|
|
7741
|
-
return
|
|
7654
|
+
return jsonResponse6(
|
|
7742
7655
|
{ error: err2 instanceof Error ? err2.message : "Token verification failed" },
|
|
7743
7656
|
401
|
|
7744
7657
|
);
|
|
@@ -7748,27 +7661,96 @@ function createOneTapModule(config, db, sessionManager) {
|
|
|
7748
7661
|
user = await findOrCreateUser(googleUser);
|
|
7749
7662
|
} catch (err2) {
|
|
7750
7663
|
if (err2 instanceof OneTapVerifyError && err2.code === "USER_NOT_FOUND") {
|
|
7751
|
-
return
|
|
7664
|
+
return jsonResponse6({ error: err2.message }, 403);
|
|
7752
7665
|
}
|
|
7753
|
-
return
|
|
7666
|
+
return jsonResponse6(
|
|
7754
7667
|
{ error: err2 instanceof Error ? err2.message : "Failed to resolve user" },
|
|
7755
7668
|
500
|
|
7756
7669
|
);
|
|
7757
7670
|
}
|
|
7758
7671
|
const { token: sessionToken, session } = await sessionManager.create(user.id);
|
|
7759
|
-
return
|
|
7672
|
+
return jsonResponse6({
|
|
7760
7673
|
user: { id: user.id, email: user.email },
|
|
7761
7674
|
session: { token: sessionToken, expiresAt: session.expiresAt }
|
|
7762
7675
|
});
|
|
7763
7676
|
}
|
|
7764
7677
|
return { verify, handleRequest };
|
|
7765
7678
|
}
|
|
7766
|
-
function
|
|
7679
|
+
function jsonResponse6(body, status = 200) {
|
|
7767
7680
|
return new Response(JSON.stringify(body), {
|
|
7768
7681
|
status,
|
|
7769
7682
|
headers: { "Content-Type": "application/json" }
|
|
7770
7683
|
});
|
|
7771
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
|
+
}
|
|
7772
7754
|
|
|
7773
7755
|
// src/auth/one-tap-plugin.ts
|
|
7774
7756
|
function oneTap(config) {
|
|
@@ -9060,14 +9042,14 @@ function rowToInvitation(row) {
|
|
|
9060
9042
|
createdAt: row.createdAt
|
|
9061
9043
|
};
|
|
9062
9044
|
}
|
|
9063
|
-
function
|
|
9045
|
+
function jsonResponse7(body, status = 200) {
|
|
9064
9046
|
return new Response(JSON.stringify(body), {
|
|
9065
9047
|
status,
|
|
9066
9048
|
headers: { "Content-Type": "application/json" }
|
|
9067
9049
|
});
|
|
9068
9050
|
}
|
|
9069
9051
|
function errorResponse(message, status) {
|
|
9070
|
-
return
|
|
9052
|
+
return jsonResponse7({ error: message }, status);
|
|
9071
9053
|
}
|
|
9072
9054
|
function createOrgModule(config, db) {
|
|
9073
9055
|
const maxMembers = config.maxMembers ?? DEFAULT_MAX_MEMBERS;
|
|
@@ -9373,7 +9355,7 @@ function createOrgModule(config, db) {
|
|
|
9373
9355
|
try {
|
|
9374
9356
|
const body = await request.json();
|
|
9375
9357
|
const org = await create(body);
|
|
9376
|
-
return
|
|
9358
|
+
return jsonResponse7(org, 201);
|
|
9377
9359
|
} catch (err2) {
|
|
9378
9360
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9379
9361
|
}
|
|
@@ -9383,7 +9365,7 @@ function createOrgModule(config, db) {
|
|
|
9383
9365
|
const userId = userOrgMatch[1];
|
|
9384
9366
|
if (!userId) return errorResponse("Missing userId", 400);
|
|
9385
9367
|
const orgs = await list(userId);
|
|
9386
|
-
return
|
|
9368
|
+
return jsonResponse7(orgs);
|
|
9387
9369
|
}
|
|
9388
9370
|
const orgBaseMatch = pathname.match(/^\/auth\/org\/([^/]+)(\/.*)?$/);
|
|
9389
9371
|
if (!orgBaseMatch) return null;
|
|
@@ -9393,13 +9375,13 @@ function createOrgModule(config, db) {
|
|
|
9393
9375
|
if (method === "GET" && subPath === "") {
|
|
9394
9376
|
const org = await get(orgId);
|
|
9395
9377
|
if (!org) return errorResponse("Organization not found", 404);
|
|
9396
|
-
return
|
|
9378
|
+
return jsonResponse7(org);
|
|
9397
9379
|
}
|
|
9398
9380
|
if (method === "PATCH" && subPath === "") {
|
|
9399
9381
|
try {
|
|
9400
9382
|
const body = await request.json();
|
|
9401
9383
|
const org = await update(orgId, body);
|
|
9402
|
-
return
|
|
9384
|
+
return jsonResponse7(org);
|
|
9403
9385
|
} catch (err2) {
|
|
9404
9386
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9405
9387
|
}
|
|
@@ -9407,7 +9389,7 @@ function createOrgModule(config, db) {
|
|
|
9407
9389
|
if (method === "DELETE" && subPath === "") {
|
|
9408
9390
|
try {
|
|
9409
9391
|
await remove(orgId);
|
|
9410
|
-
return
|
|
9392
|
+
return jsonResponse7({ success: true });
|
|
9411
9393
|
} catch (err2) {
|
|
9412
9394
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9413
9395
|
}
|
|
@@ -9416,14 +9398,14 @@ function createOrgModule(config, db) {
|
|
|
9416
9398
|
try {
|
|
9417
9399
|
const body = await request.json();
|
|
9418
9400
|
const member = await addMember(orgId, body.userId, body.role);
|
|
9419
|
-
return
|
|
9401
|
+
return jsonResponse7(member, 201);
|
|
9420
9402
|
} catch (err2) {
|
|
9421
9403
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9422
9404
|
}
|
|
9423
9405
|
}
|
|
9424
9406
|
if (method === "GET" && subPath === "/members") {
|
|
9425
9407
|
const members = await getMembers(orgId);
|
|
9426
|
-
return
|
|
9408
|
+
return jsonResponse7(members);
|
|
9427
9409
|
}
|
|
9428
9410
|
const memberMatch = subPath.match(/^\/members\/([^/]+)$/);
|
|
9429
9411
|
if (method === "PATCH" && memberMatch) {
|
|
@@ -9432,7 +9414,7 @@ function createOrgModule(config, db) {
|
|
|
9432
9414
|
try {
|
|
9433
9415
|
const body = await request.json();
|
|
9434
9416
|
const member = await updateMemberRole(orgId, userId, body.role);
|
|
9435
|
-
return
|
|
9417
|
+
return jsonResponse7(member);
|
|
9436
9418
|
} catch (err2) {
|
|
9437
9419
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9438
9420
|
}
|
|
@@ -9441,20 +9423,20 @@ function createOrgModule(config, db) {
|
|
|
9441
9423
|
const userId = memberMatch[1];
|
|
9442
9424
|
if (!userId) return errorResponse("Missing userId", 400);
|
|
9443
9425
|
await removeMember(orgId, userId);
|
|
9444
|
-
return
|
|
9426
|
+
return jsonResponse7({ success: true });
|
|
9445
9427
|
}
|
|
9446
9428
|
if (method === "POST" && subPath === "/invite") {
|
|
9447
9429
|
try {
|
|
9448
9430
|
const body = await request.json();
|
|
9449
9431
|
const invitation = await invite({ orgId, ...body });
|
|
9450
|
-
return
|
|
9432
|
+
return jsonResponse7(invitation, 201);
|
|
9451
9433
|
} catch (err2) {
|
|
9452
9434
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9453
9435
|
}
|
|
9454
9436
|
}
|
|
9455
9437
|
if (method === "GET" && subPath === "/invitations") {
|
|
9456
9438
|
const invitations = await listInvitations(orgId);
|
|
9457
|
-
return
|
|
9439
|
+
return jsonResponse7(invitations);
|
|
9458
9440
|
}
|
|
9459
9441
|
const permMatch = subPath.match(/^\/permissions\/([^/]+)\/([^/]+)$/);
|
|
9460
9442
|
if (method === "GET" && permMatch) {
|
|
@@ -9462,20 +9444,20 @@ function createOrgModule(config, db) {
|
|
|
9462
9444
|
const permission = permMatch[2];
|
|
9463
9445
|
if (!userId || !permission) return errorResponse("Missing userId or permission", 400);
|
|
9464
9446
|
const allowed = await hasPermission(orgId, userId, permission);
|
|
9465
|
-
return
|
|
9447
|
+
return jsonResponse7({ allowed });
|
|
9466
9448
|
}
|
|
9467
9449
|
if (method === "POST" && subPath === "/roles") {
|
|
9468
9450
|
try {
|
|
9469
9451
|
const body = await request.json();
|
|
9470
9452
|
const role = await createRole(orgId, body);
|
|
9471
|
-
return
|
|
9453
|
+
return jsonResponse7(role, 201);
|
|
9472
9454
|
} catch (err2) {
|
|
9473
9455
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9474
9456
|
}
|
|
9475
9457
|
}
|
|
9476
9458
|
if (method === "GET" && subPath === "/roles") {
|
|
9477
9459
|
const roles = await getRoles(orgId);
|
|
9478
|
-
return
|
|
9460
|
+
return jsonResponse7(roles);
|
|
9479
9461
|
}
|
|
9480
9462
|
return null;
|
|
9481
9463
|
}
|
|
@@ -9490,7 +9472,7 @@ function createOrgModule(config, db) {
|
|
|
9490
9472
|
try {
|
|
9491
9473
|
const body = await request.json();
|
|
9492
9474
|
const member = await acceptInvitation(invitationId, body.userId);
|
|
9493
|
-
return
|
|
9475
|
+
return jsonResponse7(member, 201);
|
|
9494
9476
|
} catch (err2) {
|
|
9495
9477
|
return errorResponse(err2 instanceof Error ? err2.message : "Unknown error", 400);
|
|
9496
9478
|
}
|
|
@@ -9500,7 +9482,7 @@ function createOrgModule(config, db) {
|
|
|
9500
9482
|
const invitationId = revokeMatch[1];
|
|
9501
9483
|
if (!invitationId) return errorResponse("Missing invitationId", 400);
|
|
9502
9484
|
await revokeInvitation(invitationId);
|
|
9503
|
-
return
|
|
9485
|
+
return jsonResponse7({ success: true });
|
|
9504
9486
|
}
|
|
9505
9487
|
return handleRequest(request);
|
|
9506
9488
|
}
|
|
@@ -9531,13 +9513,13 @@ function createOrgModule(config, db) {
|
|
|
9531
9513
|
}
|
|
9532
9514
|
|
|
9533
9515
|
// src/auth/organization-plugin.ts
|
|
9534
|
-
function
|
|
9516
|
+
function jsonResponse8(body, status = 200) {
|
|
9535
9517
|
return new Response(JSON.stringify(body), {
|
|
9536
9518
|
status,
|
|
9537
9519
|
headers: { "Content-Type": "application/json" }
|
|
9538
9520
|
});
|
|
9539
9521
|
}
|
|
9540
|
-
async function
|
|
9522
|
+
async function parseBody3(request) {
|
|
9541
9523
|
try {
|
|
9542
9524
|
return await request.json();
|
|
9543
9525
|
} catch {
|
|
@@ -9560,20 +9542,20 @@ function organization(config) {
|
|
|
9560
9542
|
async handler(request, endpointCtx) {
|
|
9561
9543
|
const user = await endpointCtx.getUser(request);
|
|
9562
9544
|
if (!user) {
|
|
9563
|
-
return
|
|
9545
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9564
9546
|
}
|
|
9565
|
-
const body = await
|
|
9547
|
+
const body = await parseBody3(request);
|
|
9566
9548
|
const name = typeof body.name === "string" ? body.name.trim() : null;
|
|
9567
9549
|
const slug = typeof body.slug === "string" ? body.slug.trim() : null;
|
|
9568
9550
|
if (!name || !slug) {
|
|
9569
|
-
return
|
|
9551
|
+
return jsonResponse8({ error: "Missing required fields: name, slug" }, 400);
|
|
9570
9552
|
}
|
|
9571
9553
|
const metadata = body.metadata !== void 0 && typeof body.metadata === "object" && body.metadata !== null ? body.metadata : void 0;
|
|
9572
9554
|
try {
|
|
9573
9555
|
const org = await module.create({ name, slug, ownerId: user.id, metadata });
|
|
9574
|
-
return
|
|
9556
|
+
return jsonResponse8(org, 201);
|
|
9575
9557
|
} catch (err2) {
|
|
9576
|
-
return
|
|
9558
|
+
return jsonResponse8(
|
|
9577
9559
|
{ error: err2 instanceof Error ? err2.message : "Failed to create organization" },
|
|
9578
9560
|
400
|
|
9579
9561
|
);
|
|
@@ -9590,10 +9572,10 @@ function organization(config) {
|
|
|
9590
9572
|
async handler(request, endpointCtx) {
|
|
9591
9573
|
const user = await endpointCtx.getUser(request);
|
|
9592
9574
|
if (!user) {
|
|
9593
|
-
return
|
|
9575
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9594
9576
|
}
|
|
9595
9577
|
const orgs = await module.list(user.id);
|
|
9596
|
-
return
|
|
9578
|
+
return jsonResponse8({ organizations: orgs });
|
|
9597
9579
|
}
|
|
9598
9580
|
});
|
|
9599
9581
|
ctx.addEndpoint({
|
|
@@ -9606,23 +9588,23 @@ function organization(config) {
|
|
|
9606
9588
|
async handler(request, endpointCtx) {
|
|
9607
9589
|
const user = await endpointCtx.getUser(request);
|
|
9608
9590
|
if (!user) {
|
|
9609
|
-
return
|
|
9591
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9610
9592
|
}
|
|
9611
9593
|
const url = new URL(request.url);
|
|
9612
9594
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9613
9595
|
const orgId = segments[2];
|
|
9614
9596
|
if (!orgId) {
|
|
9615
|
-
return
|
|
9597
|
+
return jsonResponse8({ error: "Missing organization ID in path" }, 400);
|
|
9616
9598
|
}
|
|
9617
9599
|
const member = await module.getMember(orgId, user.id);
|
|
9618
9600
|
if (!member || !ADMIN_ROLES.has(member.role)) {
|
|
9619
|
-
return
|
|
9601
|
+
return jsonResponse8({ error: "Admin or owner role required" }, 403);
|
|
9620
9602
|
}
|
|
9621
|
-
const body = await
|
|
9603
|
+
const body = await parseBody3(request);
|
|
9622
9604
|
const email = typeof body.email === "string" ? body.email.trim().toLowerCase() : null;
|
|
9623
9605
|
const role = typeof body.role === "string" ? body.role : "member";
|
|
9624
9606
|
if (!email) {
|
|
9625
|
-
return
|
|
9607
|
+
return jsonResponse8({ error: "Missing required field: email" }, 400);
|
|
9626
9608
|
}
|
|
9627
9609
|
try {
|
|
9628
9610
|
const invitation = await module.invite({
|
|
@@ -9631,9 +9613,9 @@ function organization(config) {
|
|
|
9631
9613
|
role,
|
|
9632
9614
|
invitedBy: user.id
|
|
9633
9615
|
});
|
|
9634
|
-
return
|
|
9616
|
+
return jsonResponse8(invitation, 201);
|
|
9635
9617
|
} catch (err2) {
|
|
9636
|
-
return
|
|
9618
|
+
return jsonResponse8(
|
|
9637
9619
|
{ error: err2 instanceof Error ? err2.message : "Failed to send invitation" },
|
|
9638
9620
|
400
|
|
9639
9621
|
);
|
|
@@ -9650,20 +9632,20 @@ function organization(config) {
|
|
|
9650
9632
|
async handler(request, endpointCtx) {
|
|
9651
9633
|
const user = await endpointCtx.getUser(request);
|
|
9652
9634
|
if (!user) {
|
|
9653
|
-
return
|
|
9635
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9654
9636
|
}
|
|
9655
9637
|
const url = new URL(request.url);
|
|
9656
9638
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9657
9639
|
const orgId = segments[2];
|
|
9658
9640
|
if (!orgId) {
|
|
9659
|
-
return
|
|
9641
|
+
return jsonResponse8({ error: "Missing organization ID in path" }, 400);
|
|
9660
9642
|
}
|
|
9661
9643
|
const callerMember = await module.getMember(orgId, user.id);
|
|
9662
9644
|
if (!callerMember) {
|
|
9663
|
-
return
|
|
9645
|
+
return jsonResponse8({ error: "You are not a member of this organization" }, 403);
|
|
9664
9646
|
}
|
|
9665
9647
|
const members = await module.getMembers(orgId);
|
|
9666
|
-
return
|
|
9648
|
+
return jsonResponse8({ members });
|
|
9667
9649
|
}
|
|
9668
9650
|
});
|
|
9669
9651
|
ctx.addEndpoint({
|
|
@@ -9676,29 +9658,29 @@ function organization(config) {
|
|
|
9676
9658
|
async handler(request, endpointCtx) {
|
|
9677
9659
|
const user = await endpointCtx.getUser(request);
|
|
9678
9660
|
if (!user) {
|
|
9679
|
-
return
|
|
9661
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9680
9662
|
}
|
|
9681
9663
|
const url = new URL(request.url);
|
|
9682
9664
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9683
9665
|
const orgId = segments[2];
|
|
9684
9666
|
const targetUserId = segments[4];
|
|
9685
9667
|
if (!orgId || !targetUserId) {
|
|
9686
|
-
return
|
|
9668
|
+
return jsonResponse8({ error: "Missing organization ID or user ID in path" }, 400);
|
|
9687
9669
|
}
|
|
9688
9670
|
const callerMember = await module.getMember(orgId, user.id);
|
|
9689
9671
|
if (!callerMember || !ADMIN_ROLES.has(callerMember.role)) {
|
|
9690
|
-
return
|
|
9672
|
+
return jsonResponse8({ error: "Admin or owner role required" }, 403);
|
|
9691
9673
|
}
|
|
9692
|
-
const body = await
|
|
9674
|
+
const body = await parseBody3(request);
|
|
9693
9675
|
const role = typeof body.role === "string" ? body.role : null;
|
|
9694
9676
|
if (!role) {
|
|
9695
|
-
return
|
|
9677
|
+
return jsonResponse8({ error: "Missing required field: role" }, 400);
|
|
9696
9678
|
}
|
|
9697
9679
|
try {
|
|
9698
9680
|
const member = await module.updateMemberRole(orgId, targetUserId, role);
|
|
9699
|
-
return
|
|
9681
|
+
return jsonResponse8(member);
|
|
9700
9682
|
} catch (err2) {
|
|
9701
|
-
return
|
|
9683
|
+
return jsonResponse8(
|
|
9702
9684
|
{ error: err2 instanceof Error ? err2.message : "Failed to update member role" },
|
|
9703
9685
|
400
|
|
9704
9686
|
);
|
|
@@ -9715,24 +9697,24 @@ function organization(config) {
|
|
|
9715
9697
|
async handler(request, endpointCtx) {
|
|
9716
9698
|
const user = await endpointCtx.getUser(request);
|
|
9717
9699
|
if (!user) {
|
|
9718
|
-
return
|
|
9700
|
+
return jsonResponse8({ error: "Authentication required" }, 401);
|
|
9719
9701
|
}
|
|
9720
9702
|
const url = new URL(request.url);
|
|
9721
9703
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
9722
9704
|
const orgId = segments[2];
|
|
9723
9705
|
const targetUserId = segments[4];
|
|
9724
9706
|
if (!orgId || !targetUserId) {
|
|
9725
|
-
return
|
|
9707
|
+
return jsonResponse8({ error: "Missing organization ID or user ID in path" }, 400);
|
|
9726
9708
|
}
|
|
9727
9709
|
const callerMember = await module.getMember(orgId, user.id);
|
|
9728
9710
|
if (!callerMember || !ADMIN_ROLES.has(callerMember.role)) {
|
|
9729
|
-
return
|
|
9711
|
+
return jsonResponse8({ error: "Admin or owner role required" }, 403);
|
|
9730
9712
|
}
|
|
9731
9713
|
try {
|
|
9732
9714
|
await module.removeMember(orgId, targetUserId);
|
|
9733
|
-
return
|
|
9715
|
+
return jsonResponse8({ removed: true });
|
|
9734
9716
|
} catch (err2) {
|
|
9735
|
-
return
|
|
9717
|
+
return jsonResponse8(
|
|
9736
9718
|
{ error: err2 instanceof Error ? err2.message : "Failed to remove member" },
|
|
9737
9719
|
400
|
|
9738
9720
|
);
|
|
@@ -10137,13 +10119,13 @@ function parseAuthData(authData) {
|
|
|
10137
10119
|
}
|
|
10138
10120
|
return { rpIdHash, flags, signCount, attestedCredentialData };
|
|
10139
10121
|
}
|
|
10140
|
-
function
|
|
10122
|
+
function jsonResponse9(body, status = 200) {
|
|
10141
10123
|
return new Response(JSON.stringify(body), {
|
|
10142
10124
|
status,
|
|
10143
10125
|
headers: { "Content-Type": "application/json" }
|
|
10144
10126
|
});
|
|
10145
10127
|
}
|
|
10146
|
-
async function
|
|
10128
|
+
async function parseBody4(request) {
|
|
10147
10129
|
try {
|
|
10148
10130
|
return await request.json();
|
|
10149
10131
|
} catch {
|
|
@@ -10481,84 +10463,84 @@ function createPasskeyModule(config, db) {
|
|
|
10481
10463
|
const method = request.method.toUpperCase();
|
|
10482
10464
|
const segments = getPathSegments(url);
|
|
10483
10465
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "register" && segments[3] === "options") {
|
|
10484
|
-
const body = await
|
|
10466
|
+
const body = await parseBody4(request);
|
|
10485
10467
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
10486
10468
|
const userName = typeof body.userName === "string" ? body.userName : null;
|
|
10487
10469
|
if (!userId || !userName) {
|
|
10488
|
-
return
|
|
10470
|
+
return jsonResponse9({ error: "userId and userName required" }, 400);
|
|
10489
10471
|
}
|
|
10490
10472
|
try {
|
|
10491
10473
|
const options = await getRegistrationOptions(userId, userName);
|
|
10492
|
-
return
|
|
10474
|
+
return jsonResponse9(options);
|
|
10493
10475
|
} catch (err2) {
|
|
10494
10476
|
const message = err2 instanceof Error ? err2.message : "Failed to generate options";
|
|
10495
10477
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10496
|
-
return
|
|
10478
|
+
return jsonResponse9({ error: message, code: code2 }, 500);
|
|
10497
10479
|
}
|
|
10498
10480
|
}
|
|
10499
10481
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "register" && segments[3] === "verify") {
|
|
10500
|
-
const body = await
|
|
10482
|
+
const body = await parseBody4(request);
|
|
10501
10483
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
10502
|
-
if (!userId) return
|
|
10484
|
+
if (!userId) return jsonResponse9({ error: "userId required" }, 400);
|
|
10503
10485
|
const resp = body.response;
|
|
10504
|
-
if (!resp) return
|
|
10486
|
+
if (!resp) return jsonResponse9({ error: "response required" }, 400);
|
|
10505
10487
|
try {
|
|
10506
10488
|
const result = await verifyRegistration(userId, resp);
|
|
10507
|
-
return
|
|
10489
|
+
return jsonResponse9(result);
|
|
10508
10490
|
} catch (err2) {
|
|
10509
10491
|
const message = err2 instanceof Error ? err2.message : "Registration failed";
|
|
10510
10492
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10511
|
-
return
|
|
10493
|
+
return jsonResponse9({ error: message, code: code2 }, 400);
|
|
10512
10494
|
}
|
|
10513
10495
|
}
|
|
10514
10496
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "login" && segments[3] === "options") {
|
|
10515
|
-
const body = await
|
|
10497
|
+
const body = await parseBody4(request);
|
|
10516
10498
|
const userId = typeof body.userId === "string" ? body.userId : void 0;
|
|
10517
10499
|
try {
|
|
10518
10500
|
const options = await getAuthenticationOptions(userId);
|
|
10519
|
-
return
|
|
10501
|
+
return jsonResponse9(options);
|
|
10520
10502
|
} catch (err2) {
|
|
10521
10503
|
const message = err2 instanceof Error ? err2.message : "Failed to generate options";
|
|
10522
10504
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10523
|
-
return
|
|
10505
|
+
return jsonResponse9({ error: message, code: code2 }, 500);
|
|
10524
10506
|
}
|
|
10525
10507
|
}
|
|
10526
10508
|
if (method === "POST" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "login" && segments[3] === "verify") {
|
|
10527
|
-
const body = await
|
|
10509
|
+
const body = await parseBody4(request);
|
|
10528
10510
|
const resp = body.response;
|
|
10529
|
-
if (!resp) return
|
|
10511
|
+
if (!resp) return jsonResponse9({ error: "response required" }, 400);
|
|
10530
10512
|
try {
|
|
10531
10513
|
const result = await verifyAuthentication(resp);
|
|
10532
|
-
return
|
|
10514
|
+
return jsonResponse9(result);
|
|
10533
10515
|
} catch (err2) {
|
|
10534
10516
|
const message = err2 instanceof Error ? err2.message : "Authentication failed";
|
|
10535
10517
|
const code2 = err2 instanceof PasskeyError ? err2.code : "INTERNAL_ERROR";
|
|
10536
|
-
return
|
|
10518
|
+
return jsonResponse9({ error: message, code: code2 }, 401);
|
|
10537
10519
|
}
|
|
10538
10520
|
}
|
|
10539
10521
|
if (method === "GET" && segments.length === 3 && segments[1] === "passkey" && segments[2] === "credentials") {
|
|
10540
10522
|
const userId = url.searchParams.get("userId");
|
|
10541
|
-
if (!userId) return
|
|
10523
|
+
if (!userId) return jsonResponse9({ error: "userId query param required" }, 400);
|
|
10542
10524
|
try {
|
|
10543
10525
|
const creds = await listCredentials(userId);
|
|
10544
|
-
return
|
|
10526
|
+
return jsonResponse9({ credentials: creds });
|
|
10545
10527
|
} catch (err2) {
|
|
10546
10528
|
const message = err2 instanceof Error ? err2.message : "Failed to list credentials";
|
|
10547
|
-
return
|
|
10529
|
+
return jsonResponse9({ error: message }, 500);
|
|
10548
10530
|
}
|
|
10549
10531
|
}
|
|
10550
10532
|
if (method === "DELETE" && segments.length === 4 && segments[1] === "passkey" && segments[2] === "credentials") {
|
|
10551
10533
|
const credentialId = segments[3];
|
|
10552
|
-
if (!credentialId) return
|
|
10553
|
-
const body = await
|
|
10534
|
+
if (!credentialId) return jsonResponse9({ error: "Credential ID required" }, 400);
|
|
10535
|
+
const body = await parseBody4(request);
|
|
10554
10536
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
10555
|
-
if (!userId) return
|
|
10537
|
+
if (!userId) return jsonResponse9({ error: "userId required" }, 400);
|
|
10556
10538
|
try {
|
|
10557
10539
|
await removeCredential(credentialId, userId);
|
|
10558
|
-
return
|
|
10540
|
+
return jsonResponse9({ removed: true });
|
|
10559
10541
|
} catch (err2) {
|
|
10560
10542
|
const message = err2 instanceof Error ? err2.message : "Failed to remove credential";
|
|
10561
|
-
return
|
|
10543
|
+
return jsonResponse9({ error: message }, 500);
|
|
10562
10544
|
}
|
|
10563
10545
|
}
|
|
10564
10546
|
return null;
|
|
@@ -10575,13 +10557,13 @@ function createPasskeyModule(config, db) {
|
|
|
10575
10557
|
}
|
|
10576
10558
|
|
|
10577
10559
|
// src/auth/passkey-plugin.ts
|
|
10578
|
-
function
|
|
10560
|
+
function jsonResponse10(body, status = 200) {
|
|
10579
10561
|
return new Response(JSON.stringify(body), {
|
|
10580
10562
|
status,
|
|
10581
10563
|
headers: { "Content-Type": "application/json" }
|
|
10582
10564
|
});
|
|
10583
10565
|
}
|
|
10584
|
-
async function
|
|
10566
|
+
async function parseBody5(request) {
|
|
10585
10567
|
try {
|
|
10586
10568
|
return await request.json();
|
|
10587
10569
|
} catch {
|
|
@@ -10603,16 +10585,16 @@ function passkey(config) {
|
|
|
10603
10585
|
async handler(request, endpointCtx) {
|
|
10604
10586
|
const user = await endpointCtx.getUser(request);
|
|
10605
10587
|
if (!user) {
|
|
10606
|
-
return
|
|
10588
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10607
10589
|
}
|
|
10608
|
-
const body = await
|
|
10590
|
+
const body = await parseBody5(request);
|
|
10609
10591
|
const userId = typeof body.userId === "string" ? body.userId : user.id;
|
|
10610
10592
|
const userName = typeof body.userName === "string" ? body.userName : user.email ?? user.id;
|
|
10611
10593
|
try {
|
|
10612
10594
|
const options = await module.getRegistrationOptions(userId, userName);
|
|
10613
|
-
return
|
|
10595
|
+
return jsonResponse10(options);
|
|
10614
10596
|
} catch (err2) {
|
|
10615
|
-
return
|
|
10597
|
+
return jsonResponse10(
|
|
10616
10598
|
{ error: err2 instanceof Error ? err2.message : "Failed to generate options" },
|
|
10617
10599
|
500
|
|
10618
10600
|
);
|
|
@@ -10629,19 +10611,19 @@ function passkey(config) {
|
|
|
10629
10611
|
async handler(request, endpointCtx) {
|
|
10630
10612
|
const user = await endpointCtx.getUser(request);
|
|
10631
10613
|
if (!user) {
|
|
10632
|
-
return
|
|
10614
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10633
10615
|
}
|
|
10634
|
-
const body = await
|
|
10616
|
+
const body = await parseBody5(request);
|
|
10635
10617
|
const userId = typeof body.userId === "string" ? body.userId : user.id;
|
|
10636
10618
|
const response = body.response;
|
|
10637
10619
|
if (!response) {
|
|
10638
|
-
return
|
|
10620
|
+
return jsonResponse10({ error: "Missing required field: response" }, 400);
|
|
10639
10621
|
}
|
|
10640
10622
|
try {
|
|
10641
10623
|
const result = await module.verifyRegistration(userId, response);
|
|
10642
|
-
return
|
|
10624
|
+
return jsonResponse10(result);
|
|
10643
10625
|
} catch (err2) {
|
|
10644
|
-
return
|
|
10626
|
+
return jsonResponse10(
|
|
10645
10627
|
{ error: err2 instanceof Error ? err2.message : "Registration failed" },
|
|
10646
10628
|
400
|
|
10647
10629
|
);
|
|
@@ -10655,13 +10637,13 @@ function passkey(config) {
|
|
|
10655
10637
|
description: "Get WebAuthn authentication options"
|
|
10656
10638
|
},
|
|
10657
10639
|
async handler(request) {
|
|
10658
|
-
const body = await
|
|
10640
|
+
const body = await parseBody5(request);
|
|
10659
10641
|
const userId = typeof body.userId === "string" ? body.userId : void 0;
|
|
10660
10642
|
try {
|
|
10661
10643
|
const options = await module.getAuthenticationOptions(userId);
|
|
10662
|
-
return
|
|
10644
|
+
return jsonResponse10(options);
|
|
10663
10645
|
} catch (err2) {
|
|
10664
|
-
return
|
|
10646
|
+
return jsonResponse10(
|
|
10665
10647
|
{ error: err2 instanceof Error ? err2.message : "Failed to generate options" },
|
|
10666
10648
|
500
|
|
10667
10649
|
);
|
|
@@ -10675,19 +10657,37 @@ function passkey(config) {
|
|
|
10675
10657
|
description: "Verify a WebAuthn assertion and return the authenticated user"
|
|
10676
10658
|
},
|
|
10677
10659
|
async handler(request) {
|
|
10678
|
-
const body = await
|
|
10660
|
+
const body = await parseBody5(request);
|
|
10679
10661
|
const response = body.response;
|
|
10680
10662
|
if (!response) {
|
|
10681
|
-
return
|
|
10663
|
+
return jsonResponse10({ error: "Missing required field: response" }, 400);
|
|
10682
10664
|
}
|
|
10683
10665
|
try {
|
|
10684
10666
|
const result = await module.verifyAuthentication(response);
|
|
10685
10667
|
if (!result) {
|
|
10686
|
-
return
|
|
10668
|
+
return jsonResponse10({ error: "Authentication failed" }, 401);
|
|
10687
10669
|
}
|
|
10688
|
-
|
|
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);
|
|
10689
10689
|
} catch (err2) {
|
|
10690
|
-
return
|
|
10690
|
+
return jsonResponse10(
|
|
10691
10691
|
{ error: err2 instanceof Error ? err2.message : "Authentication failed" },
|
|
10692
10692
|
401
|
|
10693
10693
|
);
|
|
@@ -10704,13 +10704,13 @@ function passkey(config) {
|
|
|
10704
10704
|
async handler(request, endpointCtx) {
|
|
10705
10705
|
const user = await endpointCtx.getUser(request);
|
|
10706
10706
|
if (!user) {
|
|
10707
|
-
return
|
|
10707
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10708
10708
|
}
|
|
10709
10709
|
try {
|
|
10710
10710
|
const credentials = await module.listCredentials(user.id);
|
|
10711
|
-
return
|
|
10711
|
+
return jsonResponse10({ credentials });
|
|
10712
10712
|
} catch (err2) {
|
|
10713
|
-
return
|
|
10713
|
+
return jsonResponse10(
|
|
10714
10714
|
{ error: err2 instanceof Error ? err2.message : "Failed to list credentials" },
|
|
10715
10715
|
500
|
|
10716
10716
|
);
|
|
@@ -10727,19 +10727,19 @@ function passkey(config) {
|
|
|
10727
10727
|
async handler(request, endpointCtx) {
|
|
10728
10728
|
const user = await endpointCtx.getUser(request);
|
|
10729
10729
|
if (!user) {
|
|
10730
|
-
return
|
|
10730
|
+
return jsonResponse10({ error: "Authentication required" }, 401);
|
|
10731
10731
|
}
|
|
10732
10732
|
const url = new URL(request.url);
|
|
10733
10733
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
10734
10734
|
const credentialId = segments[3];
|
|
10735
10735
|
if (!credentialId) {
|
|
10736
|
-
return
|
|
10736
|
+
return jsonResponse10({ error: "Missing credential ID in path" }, 400);
|
|
10737
10737
|
}
|
|
10738
10738
|
try {
|
|
10739
10739
|
await module.removeCredential(credentialId, user.id);
|
|
10740
|
-
return
|
|
10740
|
+
return jsonResponse10({ removed: true });
|
|
10741
10741
|
} catch (err2) {
|
|
10742
|
-
return
|
|
10742
|
+
return jsonResponse10(
|
|
10743
10743
|
{ error: err2 instanceof Error ? err2.message : "Failed to remove credential" },
|
|
10744
10744
|
500
|
|
10745
10745
|
);
|
|
@@ -10756,7 +10756,7 @@ var TOKEN_PURPOSE2 = "password-reset";
|
|
|
10756
10756
|
function makeError7(code2, message, details) {
|
|
10757
10757
|
return { code: code2, message, ...details !== void 0 ? { details } : {} };
|
|
10758
10758
|
}
|
|
10759
|
-
function
|
|
10759
|
+
function jsonResponse11(body, status = 200) {
|
|
10760
10760
|
return new Response(JSON.stringify(body), {
|
|
10761
10761
|
status,
|
|
10762
10762
|
headers: { "Content-Type": "application/json" }
|
|
@@ -10872,27 +10872,27 @@ function createPasswordResetModule(config, db, sessionManager, tokenModule) {
|
|
|
10872
10872
|
try {
|
|
10873
10873
|
body = await request.json();
|
|
10874
10874
|
} catch {
|
|
10875
|
-
return
|
|
10875
|
+
return jsonResponse11({ error: "Invalid JSON body" }, 400);
|
|
10876
10876
|
}
|
|
10877
10877
|
const b = body;
|
|
10878
10878
|
if (pathname === "/auth/forgot-password") {
|
|
10879
10879
|
if (typeof b.email !== "string") {
|
|
10880
|
-
return
|
|
10880
|
+
return jsonResponse11({ error: "Missing required field: email" }, 400);
|
|
10881
10881
|
}
|
|
10882
10882
|
const result = await requestReset(b.email);
|
|
10883
10883
|
if (!result.success) {
|
|
10884
|
-
return
|
|
10884
|
+
return jsonResponse11({ error: result.error.message }, 500);
|
|
10885
10885
|
}
|
|
10886
10886
|
return new Response(null, { status: 204 });
|
|
10887
10887
|
}
|
|
10888
10888
|
if (pathname === "/auth/reset-password") {
|
|
10889
10889
|
if (typeof b.token !== "string" || typeof b.password !== "string") {
|
|
10890
|
-
return
|
|
10890
|
+
return jsonResponse11({ error: "Missing required fields: token, password" }, 400);
|
|
10891
10891
|
}
|
|
10892
10892
|
const result = await resetPassword(b.token, b.password);
|
|
10893
10893
|
if (!result.success) {
|
|
10894
10894
|
const status = result.error.code === "INVALID_PASSWORD" ? 400 : 400;
|
|
10895
|
-
return
|
|
10895
|
+
return jsonResponse11({ error: result.error.message }, status);
|
|
10896
10896
|
}
|
|
10897
10897
|
return new Response(null, { status: 204 });
|
|
10898
10898
|
}
|
|
@@ -10927,7 +10927,7 @@ function generateNumericCode2(length) {
|
|
|
10927
10927
|
function normalisePhone(phone) {
|
|
10928
10928
|
return phone.replace(/\s+/g, "");
|
|
10929
10929
|
}
|
|
10930
|
-
function
|
|
10930
|
+
function jsonResponse12(body, status = 200) {
|
|
10931
10931
|
return new Response(JSON.stringify(body), {
|
|
10932
10932
|
status,
|
|
10933
10933
|
headers: { "Content-Type": "application/json" }
|
|
@@ -10999,25 +10999,25 @@ function createPhoneAuthModule(config, db, sessionManager) {
|
|
|
10999
10999
|
try {
|
|
11000
11000
|
body = await request.json();
|
|
11001
11001
|
} catch {
|
|
11002
|
-
return
|
|
11002
|
+
return jsonResponse12({ error: "Invalid JSON body" }, 400);
|
|
11003
11003
|
}
|
|
11004
11004
|
const b = body;
|
|
11005
11005
|
if (pathname === "/auth/phone/send-code") {
|
|
11006
11006
|
if (typeof b.phoneNumber !== "string") {
|
|
11007
|
-
return
|
|
11007
|
+
return jsonResponse12({ error: "Missing required field: phoneNumber" }, 400);
|
|
11008
11008
|
}
|
|
11009
11009
|
const result = await sendCode(b.phoneNumber);
|
|
11010
|
-
return
|
|
11010
|
+
return jsonResponse12(result);
|
|
11011
11011
|
}
|
|
11012
11012
|
if (pathname === "/auth/phone/verify") {
|
|
11013
11013
|
if (typeof b.phoneNumber !== "string" || typeof b.code !== "string") {
|
|
11014
|
-
return
|
|
11014
|
+
return jsonResponse12({ error: "Missing required fields: phoneNumber, code" }, 400);
|
|
11015
11015
|
}
|
|
11016
11016
|
const result = await verifyCode(b.phoneNumber, b.code);
|
|
11017
11017
|
if (!result) {
|
|
11018
|
-
return
|
|
11018
|
+
return jsonResponse12({ error: "Invalid or expired code" }, 401);
|
|
11019
11019
|
}
|
|
11020
|
-
return
|
|
11020
|
+
return jsonResponse12(result);
|
|
11021
11021
|
}
|
|
11022
11022
|
return null;
|
|
11023
11023
|
}
|
|
@@ -11037,12 +11037,12 @@ async function polarRequest(accessToken, baseUrl, method, path, body) {
|
|
|
11037
11037
|
headers,
|
|
11038
11038
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
11039
11039
|
});
|
|
11040
|
-
const
|
|
11040
|
+
const json2 = await response.json();
|
|
11041
11041
|
if (!response.ok) {
|
|
11042
|
-
const message =
|
|
11042
|
+
const message = json2.detail ?? json2.message ?? `Polar API error: ${response.status}`;
|
|
11043
11043
|
throw new Error(message);
|
|
11044
11044
|
}
|
|
11045
|
-
return
|
|
11045
|
+
return json2;
|
|
11046
11046
|
}
|
|
11047
11047
|
async function verifyWebhookSignature(payload, signatureHeader, webhookSecret) {
|
|
11048
11048
|
const prefix = "sha256=";
|
|
@@ -11265,19 +11265,6 @@ function createPolarModule(config, db) {
|
|
|
11265
11265
|
}
|
|
11266
11266
|
|
|
11267
11267
|
// src/auth/polar-plugin.ts
|
|
11268
|
-
function json(body, status = 200) {
|
|
11269
|
-
return new Response(JSON.stringify(body), {
|
|
11270
|
-
status,
|
|
11271
|
-
headers: { "Content-Type": "application/json" }
|
|
11272
|
-
});
|
|
11273
|
-
}
|
|
11274
|
-
async function parseBody10(request) {
|
|
11275
|
-
try {
|
|
11276
|
-
return await request.json();
|
|
11277
|
-
} catch {
|
|
11278
|
-
return {};
|
|
11279
|
-
}
|
|
11280
|
-
}
|
|
11281
11268
|
function polar(config) {
|
|
11282
11269
|
return {
|
|
11283
11270
|
id: "kavach-polar",
|
|
@@ -11295,13 +11282,14 @@ function polar(config) {
|
|
|
11295
11282
|
if (!user) {
|
|
11296
11283
|
return json({ error: "Authentication required" }, 401);
|
|
11297
11284
|
}
|
|
11298
|
-
const
|
|
11299
|
-
|
|
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;
|
|
11300
11288
|
if (!productId) {
|
|
11301
11289
|
return json({ error: "Missing required field: productId" }, 400);
|
|
11302
11290
|
}
|
|
11303
|
-
const successUrl = typeof
|
|
11304
|
-
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;
|
|
11305
11293
|
try {
|
|
11306
11294
|
const result = await module.createCheckout(user.id, productId, {
|
|
11307
11295
|
successUrl,
|
|
@@ -12746,13 +12734,13 @@ function scim(config) {
|
|
|
12746
12734
|
// src/auth/siwe.ts
|
|
12747
12735
|
var DEFAULT_NONCE_TTL_SECONDS = 300;
|
|
12748
12736
|
var SIWE_VERSION = "1";
|
|
12749
|
-
function
|
|
12737
|
+
function jsonResponse13(body, status = 200) {
|
|
12750
12738
|
return new Response(JSON.stringify(body), {
|
|
12751
12739
|
status,
|
|
12752
12740
|
headers: { "Content-Type": "application/json" }
|
|
12753
12741
|
});
|
|
12754
12742
|
}
|
|
12755
|
-
async function
|
|
12743
|
+
async function parseBody6(request) {
|
|
12756
12744
|
try {
|
|
12757
12745
|
return await request.json();
|
|
12758
12746
|
} catch {
|
|
@@ -12887,20 +12875,20 @@ function createSiweModule(config) {
|
|
|
12887
12875
|
const { method, pathname } = { method: request.method, pathname: url.pathname };
|
|
12888
12876
|
if (method === "GET" && pathname.endsWith("/auth/siwe/nonce")) {
|
|
12889
12877
|
const nonce = await generateNonce();
|
|
12890
|
-
return
|
|
12878
|
+
return jsonResponse13({ nonce });
|
|
12891
12879
|
}
|
|
12892
12880
|
if (method === "POST" && pathname.endsWith("/auth/siwe/verify")) {
|
|
12893
|
-
const body = await
|
|
12881
|
+
const body = await parseBody6(request);
|
|
12894
12882
|
const message = typeof body.message === "string" ? body.message : null;
|
|
12895
12883
|
const signature = typeof body.signature === "string" ? body.signature : null;
|
|
12896
12884
|
if (!message || !signature) {
|
|
12897
|
-
return
|
|
12885
|
+
return jsonResponse13({ error: "Missing required fields: message, signature" }, 400);
|
|
12898
12886
|
}
|
|
12899
12887
|
try {
|
|
12900
12888
|
const result = await verify(message, signature);
|
|
12901
|
-
return
|
|
12889
|
+
return jsonResponse13({ address: result.address, chainId: result.chainId });
|
|
12902
12890
|
} catch (err2) {
|
|
12903
|
-
return
|
|
12891
|
+
return jsonResponse13(
|
|
12904
12892
|
{ error: err2 instanceof Error ? err2.message : "Verification failed" },
|
|
12905
12893
|
400
|
|
12906
12894
|
);
|
|
@@ -13937,7 +13925,7 @@ function createSsoModule(config, db) {
|
|
|
13937
13925
|
const url = new URL(request.url);
|
|
13938
13926
|
const { pathname } = url;
|
|
13939
13927
|
const { method } = request;
|
|
13940
|
-
const
|
|
13928
|
+
const json2 = (data, status = 200) => new Response(JSON.stringify(data), {
|
|
13941
13929
|
status,
|
|
13942
13930
|
headers: { "Content-Type": "application/json" }
|
|
13943
13931
|
});
|
|
@@ -13946,14 +13934,14 @@ function createSsoModule(config, db) {
|
|
|
13946
13934
|
try {
|
|
13947
13935
|
body = await request.json();
|
|
13948
13936
|
} catch {
|
|
13949
|
-
return
|
|
13937
|
+
return json2({ error: "Invalid JSON body" }, 400);
|
|
13950
13938
|
}
|
|
13951
13939
|
const b = body;
|
|
13952
13940
|
if (typeof b.orgId !== "string" || typeof b.providerId !== "string" || typeof b.type !== "string" || typeof b.domain !== "string") {
|
|
13953
|
-
return
|
|
13941
|
+
return json2({ error: "Missing required fields: orgId, providerId, type, domain" }, 400);
|
|
13954
13942
|
}
|
|
13955
13943
|
if (b.type !== "saml" && b.type !== "oidc") {
|
|
13956
|
-
return
|
|
13944
|
+
return json2({ error: "type must be 'saml' or 'oidc'" }, 400);
|
|
13957
13945
|
}
|
|
13958
13946
|
const conn = await createConnection({
|
|
13959
13947
|
orgId: b.orgId,
|
|
@@ -13961,19 +13949,19 @@ function createSsoModule(config, db) {
|
|
|
13961
13949
|
type: b.type,
|
|
13962
13950
|
domain: b.domain
|
|
13963
13951
|
});
|
|
13964
|
-
return
|
|
13952
|
+
return json2(conn, 201);
|
|
13965
13953
|
}
|
|
13966
13954
|
const listMatch = /^\/auth\/sso\/connections\/([^/]+)$/.exec(pathname);
|
|
13967
13955
|
if (method === "GET" && listMatch) {
|
|
13968
13956
|
const orgId = decodeURIComponent(listMatch[1] ?? "");
|
|
13969
13957
|
const conns = await listConnections(orgId);
|
|
13970
|
-
return
|
|
13958
|
+
return json2(conns);
|
|
13971
13959
|
}
|
|
13972
13960
|
const deleteMatch = /^\/auth\/sso\/connections\/([^/]+)$/.exec(pathname);
|
|
13973
13961
|
if (method === "DELETE" && deleteMatch) {
|
|
13974
13962
|
const connId = decodeURIComponent(deleteMatch[1] ?? "");
|
|
13975
13963
|
await removeConnection(connId);
|
|
13976
|
-
return
|
|
13964
|
+
return json2({ success: true });
|
|
13977
13965
|
}
|
|
13978
13966
|
const samlInitMatch = /^\/auth\/sso\/saml\/([^/]+)$/.exec(pathname);
|
|
13979
13967
|
if (method === "GET" && samlInitMatch) {
|
|
@@ -13983,7 +13971,7 @@ function createSsoModule(config, db) {
|
|
|
13983
13971
|
const authUrl = await getSamlAuthUrl(connId, relayState);
|
|
13984
13972
|
return new Response(null, { status: 302, headers: { Location: authUrl } });
|
|
13985
13973
|
} catch (err2) {
|
|
13986
|
-
return
|
|
13974
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 400);
|
|
13987
13975
|
}
|
|
13988
13976
|
}
|
|
13989
13977
|
const samlAcsMatch = /^\/auth\/sso\/saml\/([^/]+)\/acs$/.exec(pathname);
|
|
@@ -13996,14 +13984,14 @@ function createSsoModule(config, db) {
|
|
|
13996
13984
|
if (typeof val !== "string") throw new Error("Missing SAMLResponse");
|
|
13997
13985
|
samlResponse = val;
|
|
13998
13986
|
} catch {
|
|
13999
|
-
return
|
|
13987
|
+
return json2({ error: "Missing or invalid SAMLResponse" }, 400);
|
|
14000
13988
|
}
|
|
14001
13989
|
try {
|
|
14002
13990
|
const result = await handleSamlResponse(connId, samlResponse);
|
|
14003
|
-
return
|
|
13991
|
+
return json2(result);
|
|
14004
13992
|
} catch (err2) {
|
|
14005
13993
|
const status = err2 instanceof SsoError && err2.code === SSO_ERROR.RATE_LIMITED ? 429 : 401;
|
|
14006
|
-
return
|
|
13994
|
+
return json2(
|
|
14007
13995
|
{
|
|
14008
13996
|
error: err2 instanceof Error ? err2.message : "SAML error",
|
|
14009
13997
|
code: err2 instanceof SsoError ? err2.code : "SAML_ERROR"
|
|
@@ -14021,20 +14009,20 @@ function createSsoModule(config, db) {
|
|
|
14021
14009
|
const authUrl = await getOidcAuthUrl(connId, state, nonce);
|
|
14022
14010
|
return new Response(null, { status: 302, headers: { Location: authUrl } });
|
|
14023
14011
|
} catch (err2) {
|
|
14024
|
-
return
|
|
14012
|
+
return json2({ error: err2 instanceof Error ? err2.message : "Unknown error" }, 400);
|
|
14025
14013
|
}
|
|
14026
14014
|
}
|
|
14027
14015
|
const oidcCbMatch = /^\/auth\/sso\/oidc\/([^/]+)\/callback$/.exec(pathname);
|
|
14028
14016
|
if (method === "GET" && oidcCbMatch) {
|
|
14029
14017
|
const connId = decodeURIComponent(oidcCbMatch[1] ?? "");
|
|
14030
14018
|
const code2 = url.searchParams.get("code");
|
|
14031
|
-
if (!code2) return
|
|
14019
|
+
if (!code2) return json2({ error: "Missing code parameter" }, 400);
|
|
14032
14020
|
try {
|
|
14033
14021
|
const result = await handleOidcCallback(connId, code2);
|
|
14034
|
-
return
|
|
14022
|
+
return json2(result);
|
|
14035
14023
|
} catch (err2) {
|
|
14036
14024
|
const status = err2 instanceof SsoError && err2.code === SSO_ERROR.RATE_LIMITED ? 429 : 401;
|
|
14037
|
-
return
|
|
14025
|
+
return json2(
|
|
14038
14026
|
{
|
|
14039
14027
|
error: err2 instanceof Error ? err2.message : "OIDC error",
|
|
14040
14028
|
code: err2 instanceof SsoError ? err2.code : "OIDC_ERROR"
|
|
@@ -14135,12 +14123,12 @@ async function stripeRequest(secretKey, apiVersion, method, path, body) {
|
|
|
14135
14123
|
headers,
|
|
14136
14124
|
body: bodyStr
|
|
14137
14125
|
});
|
|
14138
|
-
const
|
|
14126
|
+
const json2 = await response.json();
|
|
14139
14127
|
if (!response.ok) {
|
|
14140
|
-
const message =
|
|
14128
|
+
const message = json2.error?.message ?? `Stripe API error: ${response.status}`;
|
|
14141
14129
|
throw new Error(message);
|
|
14142
14130
|
}
|
|
14143
|
-
return
|
|
14131
|
+
return json2;
|
|
14144
14132
|
}
|
|
14145
14133
|
async function verifyWebhookSignature2(payload, signatureHeader, webhookSecret) {
|
|
14146
14134
|
const parts = {};
|
|
@@ -14450,19 +14438,6 @@ function createStripeModule(config, db) {
|
|
|
14450
14438
|
}
|
|
14451
14439
|
|
|
14452
14440
|
// src/auth/stripe-plugin.ts
|
|
14453
|
-
function json2(body, status = 200) {
|
|
14454
|
-
return new Response(JSON.stringify(body), {
|
|
14455
|
-
status,
|
|
14456
|
-
headers: { "Content-Type": "application/json" }
|
|
14457
|
-
});
|
|
14458
|
-
}
|
|
14459
|
-
async function parseBody12(request) {
|
|
14460
|
-
try {
|
|
14461
|
-
return await request.json();
|
|
14462
|
-
} catch {
|
|
14463
|
-
return {};
|
|
14464
|
-
}
|
|
14465
|
-
}
|
|
14466
14441
|
function stripe(config) {
|
|
14467
14442
|
return {
|
|
14468
14443
|
id: "kavach-stripe",
|
|
@@ -14478,17 +14453,18 @@ function stripe(config) {
|
|
|
14478
14453
|
async handler(request, endpointCtx) {
|
|
14479
14454
|
const user = await endpointCtx.getUser(request);
|
|
14480
14455
|
if (!user) {
|
|
14481
|
-
return
|
|
14456
|
+
return json({ error: "Authentication required" }, 401);
|
|
14482
14457
|
}
|
|
14483
|
-
const
|
|
14484
|
-
|
|
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;
|
|
14485
14461
|
if (!priceId) {
|
|
14486
|
-
return
|
|
14462
|
+
return json({ error: "Missing required field: priceId" }, 400);
|
|
14487
14463
|
}
|
|
14488
|
-
const successUrl = typeof
|
|
14489
|
-
const cancelUrl = typeof
|
|
14490
|
-
const trialDays = typeof
|
|
14491
|
-
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;
|
|
14492
14468
|
try {
|
|
14493
14469
|
const result = await module.createCheckoutSession(user.id, priceId, {
|
|
14494
14470
|
successUrl,
|
|
@@ -14496,9 +14472,9 @@ function stripe(config) {
|
|
|
14496
14472
|
trialDays,
|
|
14497
14473
|
metadata
|
|
14498
14474
|
});
|
|
14499
|
-
return
|
|
14475
|
+
return json(result);
|
|
14500
14476
|
} catch (err2) {
|
|
14501
|
-
return
|
|
14477
|
+
return json(
|
|
14502
14478
|
{
|
|
14503
14479
|
error: err2 instanceof Error ? err2.message : "Failed to create checkout session"
|
|
14504
14480
|
},
|
|
@@ -14517,18 +14493,19 @@ function stripe(config) {
|
|
|
14517
14493
|
async handler(request, endpointCtx) {
|
|
14518
14494
|
const user = await endpointCtx.getUser(request);
|
|
14519
14495
|
if (!user) {
|
|
14520
|
-
return
|
|
14496
|
+
return json({ error: "Authentication required" }, 401);
|
|
14521
14497
|
}
|
|
14522
|
-
const
|
|
14523
|
-
|
|
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;
|
|
14524
14501
|
if (!returnUrl) {
|
|
14525
|
-
return
|
|
14502
|
+
return json({ error: "Missing required field: returnUrl" }, 400);
|
|
14526
14503
|
}
|
|
14527
14504
|
try {
|
|
14528
14505
|
const result = await module.createPortalSession(user.id, returnUrl);
|
|
14529
|
-
return
|
|
14506
|
+
return json(result);
|
|
14530
14507
|
} catch (err2) {
|
|
14531
|
-
return
|
|
14508
|
+
return json(
|
|
14532
14509
|
{
|
|
14533
14510
|
error: err2 instanceof Error ? err2.message : "Failed to create portal session"
|
|
14534
14511
|
},
|
|
@@ -14547,10 +14524,10 @@ function stripe(config) {
|
|
|
14547
14524
|
async handler(request, endpointCtx) {
|
|
14548
14525
|
const user = await endpointCtx.getUser(request);
|
|
14549
14526
|
if (!user) {
|
|
14550
|
-
return
|
|
14527
|
+
return json({ error: "Authentication required" }, 401);
|
|
14551
14528
|
}
|
|
14552
14529
|
const subscription = await module.getSubscription(user.id);
|
|
14553
|
-
return
|
|
14530
|
+
return json({ subscription });
|
|
14554
14531
|
}
|
|
14555
14532
|
});
|
|
14556
14533
|
ctx.addEndpoint({
|
|
@@ -14652,13 +14629,13 @@ async function generateBackupCodes(count) {
|
|
|
14652
14629
|
}
|
|
14653
14630
|
return { plain, hashed };
|
|
14654
14631
|
}
|
|
14655
|
-
function
|
|
14632
|
+
function jsonResponse14(body, status = 200) {
|
|
14656
14633
|
return new Response(JSON.stringify(body), {
|
|
14657
14634
|
status,
|
|
14658
14635
|
headers: { "Content-Type": "application/json" }
|
|
14659
14636
|
});
|
|
14660
14637
|
}
|
|
14661
|
-
async function
|
|
14638
|
+
async function parseBody7(request) {
|
|
14662
14639
|
try {
|
|
14663
14640
|
return await request.json();
|
|
14664
14641
|
} catch {
|
|
@@ -14756,50 +14733,50 @@ function createTotpModule(config, db) {
|
|
|
14756
14733
|
const method = request.method.toUpperCase();
|
|
14757
14734
|
if (method !== "POST") return null;
|
|
14758
14735
|
if (path === "/auth/2fa/setup") {
|
|
14759
|
-
const body = await
|
|
14736
|
+
const body = await parseBody7(request);
|
|
14760
14737
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14761
|
-
if (!userId) return
|
|
14738
|
+
if (!userId) return jsonResponse14({ error: "userId required" }, 400);
|
|
14762
14739
|
try {
|
|
14763
14740
|
const result = await setup(userId);
|
|
14764
|
-
return
|
|
14741
|
+
return jsonResponse14(result);
|
|
14765
14742
|
} catch (err2) {
|
|
14766
|
-
return
|
|
14743
|
+
return jsonResponse14({ error: err2 instanceof Error ? err2.message : "Setup failed" }, 500);
|
|
14767
14744
|
}
|
|
14768
14745
|
}
|
|
14769
14746
|
if (path === "/auth/2fa/enable") {
|
|
14770
|
-
const body = await
|
|
14747
|
+
const body = await parseBody7(request);
|
|
14771
14748
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14772
14749
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14773
|
-
if (!userId || !code2) return
|
|
14750
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14774
14751
|
const result = await enable(userId, code2);
|
|
14775
|
-
return
|
|
14752
|
+
return jsonResponse14(result);
|
|
14776
14753
|
}
|
|
14777
14754
|
if (path === "/auth/2fa/verify") {
|
|
14778
|
-
const body = await
|
|
14755
|
+
const body = await parseBody7(request);
|
|
14779
14756
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14780
14757
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14781
|
-
if (!userId || !code2) return
|
|
14758
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14782
14759
|
const result = await verify(userId, code2);
|
|
14783
|
-
return
|
|
14760
|
+
return jsonResponse14(result);
|
|
14784
14761
|
}
|
|
14785
14762
|
if (path === "/auth/2fa/disable") {
|
|
14786
|
-
const body = await
|
|
14763
|
+
const body = await parseBody7(request);
|
|
14787
14764
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14788
14765
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14789
|
-
if (!userId || !code2) return
|
|
14766
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14790
14767
|
const result = await disable(userId, code2);
|
|
14791
|
-
return
|
|
14768
|
+
return jsonResponse14(result);
|
|
14792
14769
|
}
|
|
14793
14770
|
if (path === "/auth/2fa/backup-codes") {
|
|
14794
|
-
const body = await
|
|
14771
|
+
const body = await parseBody7(request);
|
|
14795
14772
|
const userId = typeof body.userId === "string" ? body.userId : null;
|
|
14796
14773
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14797
|
-
if (!userId || !code2) return
|
|
14774
|
+
if (!userId || !code2) return jsonResponse14({ error: "userId and code required" }, 400);
|
|
14798
14775
|
try {
|
|
14799
14776
|
const result = await regenerateBackupCodes(userId, code2);
|
|
14800
|
-
return
|
|
14777
|
+
return jsonResponse14(result);
|
|
14801
14778
|
} catch (err2) {
|
|
14802
|
-
return
|
|
14779
|
+
return jsonResponse14(
|
|
14803
14780
|
{ error: err2 instanceof Error ? err2.message : "Failed to regenerate codes" },
|
|
14804
14781
|
400
|
|
14805
14782
|
);
|
|
@@ -14819,13 +14796,13 @@ function createTotpModule(config, db) {
|
|
|
14819
14796
|
}
|
|
14820
14797
|
|
|
14821
14798
|
// src/auth/totp-plugin.ts
|
|
14822
|
-
function
|
|
14799
|
+
function jsonResponse15(body, status = 200) {
|
|
14823
14800
|
return new Response(JSON.stringify(body), {
|
|
14824
14801
|
status,
|
|
14825
14802
|
headers: { "Content-Type": "application/json" }
|
|
14826
14803
|
});
|
|
14827
14804
|
}
|
|
14828
|
-
async function
|
|
14805
|
+
async function parseBody8(request) {
|
|
14829
14806
|
try {
|
|
14830
14807
|
return await request.json();
|
|
14831
14808
|
} catch {
|
|
@@ -14847,13 +14824,13 @@ function twoFactor(config) {
|
|
|
14847
14824
|
async handler(request, endpointCtx) {
|
|
14848
14825
|
const user = await endpointCtx.getUser(request);
|
|
14849
14826
|
if (!user) {
|
|
14850
|
-
return
|
|
14827
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14851
14828
|
}
|
|
14852
14829
|
try {
|
|
14853
14830
|
const setup = await module.setup(user.id);
|
|
14854
|
-
return
|
|
14831
|
+
return jsonResponse15(setup);
|
|
14855
14832
|
} catch (err2) {
|
|
14856
|
-
return
|
|
14833
|
+
return jsonResponse15(
|
|
14857
14834
|
{ error: err2 instanceof Error ? err2.message : "Enrollment failed" },
|
|
14858
14835
|
500
|
|
14859
14836
|
);
|
|
@@ -14870,23 +14847,23 @@ function twoFactor(config) {
|
|
|
14870
14847
|
async handler(request, endpointCtx) {
|
|
14871
14848
|
const user = await endpointCtx.getUser(request);
|
|
14872
14849
|
if (!user) {
|
|
14873
|
-
return
|
|
14850
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14874
14851
|
}
|
|
14875
|
-
const body = await
|
|
14852
|
+
const body = await parseBody8(request);
|
|
14876
14853
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14877
14854
|
if (!code2) {
|
|
14878
|
-
return
|
|
14855
|
+
return jsonResponse15({ error: "Missing required field: code" }, 400);
|
|
14879
14856
|
}
|
|
14880
14857
|
const enabled = await module.isEnabled(user.id);
|
|
14881
14858
|
if (!enabled) {
|
|
14882
14859
|
const result2 = await module.enable(user.id, code2);
|
|
14883
14860
|
if (!result2.enabled) {
|
|
14884
|
-
return
|
|
14861
|
+
return jsonResponse15({ error: "Invalid TOTP code" }, 400);
|
|
14885
14862
|
}
|
|
14886
|
-
return
|
|
14863
|
+
return jsonResponse15({ valid: true, activated: true });
|
|
14887
14864
|
}
|
|
14888
14865
|
const result = await module.verify(user.id, code2);
|
|
14889
|
-
return
|
|
14866
|
+
return jsonResponse15(result);
|
|
14890
14867
|
}
|
|
14891
14868
|
});
|
|
14892
14869
|
ctx.addEndpoint({
|
|
@@ -14899,18 +14876,18 @@ function twoFactor(config) {
|
|
|
14899
14876
|
async handler(request, endpointCtx) {
|
|
14900
14877
|
const user = await endpointCtx.getUser(request);
|
|
14901
14878
|
if (!user) {
|
|
14902
|
-
return
|
|
14879
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14903
14880
|
}
|
|
14904
|
-
const body = await
|
|
14881
|
+
const body = await parseBody8(request);
|
|
14905
14882
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14906
14883
|
if (!code2) {
|
|
14907
|
-
return
|
|
14884
|
+
return jsonResponse15({ error: "Missing required field: code" }, 400);
|
|
14908
14885
|
}
|
|
14909
14886
|
const result = await module.disable(user.id, code2);
|
|
14910
14887
|
if (!result.disabled) {
|
|
14911
|
-
return
|
|
14888
|
+
return jsonResponse15({ error: "Invalid TOTP code" }, 400);
|
|
14912
14889
|
}
|
|
14913
|
-
return
|
|
14890
|
+
return jsonResponse15(result);
|
|
14914
14891
|
}
|
|
14915
14892
|
});
|
|
14916
14893
|
ctx.addEndpoint({
|
|
@@ -14923,10 +14900,10 @@ function twoFactor(config) {
|
|
|
14923
14900
|
async handler(request, endpointCtx) {
|
|
14924
14901
|
const user = await endpointCtx.getUser(request);
|
|
14925
14902
|
if (!user) {
|
|
14926
|
-
return
|
|
14903
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14927
14904
|
}
|
|
14928
14905
|
const enabled = await module.isEnabled(user.id);
|
|
14929
|
-
return
|
|
14906
|
+
return jsonResponse15({ enabled });
|
|
14930
14907
|
}
|
|
14931
14908
|
});
|
|
14932
14909
|
ctx.addEndpoint({
|
|
@@ -14939,18 +14916,18 @@ function twoFactor(config) {
|
|
|
14939
14916
|
async handler(request, endpointCtx) {
|
|
14940
14917
|
const user = await endpointCtx.getUser(request);
|
|
14941
14918
|
if (!user) {
|
|
14942
|
-
return
|
|
14919
|
+
return jsonResponse15({ error: "Authentication required" }, 401);
|
|
14943
14920
|
}
|
|
14944
|
-
const body = await
|
|
14921
|
+
const body = await parseBody8(request);
|
|
14945
14922
|
const code2 = typeof body.code === "string" ? body.code : null;
|
|
14946
14923
|
if (!code2) {
|
|
14947
|
-
return
|
|
14924
|
+
return jsonResponse15({ error: "Missing required field: code" }, 400);
|
|
14948
14925
|
}
|
|
14949
14926
|
try {
|
|
14950
14927
|
const result = await module.regenerateBackupCodes(user.id, code2);
|
|
14951
|
-
return
|
|
14928
|
+
return jsonResponse15(result);
|
|
14952
14929
|
} catch (err2) {
|
|
14953
|
-
return
|
|
14930
|
+
return jsonResponse15(
|
|
14954
14931
|
{ error: err2 instanceof Error ? err2.message : "Failed to regenerate backup codes" },
|
|
14955
14932
|
400
|
|
14956
14933
|
);
|
|
@@ -15069,7 +15046,7 @@ async function hashPassword(password) {
|
|
|
15069
15046
|
async function verifyPassword(stored, candidate) {
|
|
15070
15047
|
return pbkdf2Verify(candidate, stored);
|
|
15071
15048
|
}
|
|
15072
|
-
function
|
|
15049
|
+
function jsonResponse16(body, status = 200) {
|
|
15073
15050
|
return new Response(JSON.stringify(body), {
|
|
15074
15051
|
status,
|
|
15075
15052
|
headers: { "Content-Type": "application/json" }
|
|
@@ -15193,12 +15170,12 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15193
15170
|
try {
|
|
15194
15171
|
body = await request.json();
|
|
15195
15172
|
} catch {
|
|
15196
|
-
return
|
|
15173
|
+
return jsonResponse16({ error: "Invalid JSON body" }, 400);
|
|
15197
15174
|
}
|
|
15198
15175
|
const b = body;
|
|
15199
15176
|
if (pathname === "/auth/username/sign-up") {
|
|
15200
15177
|
if (typeof b.username !== "string" || typeof b.password !== "string") {
|
|
15201
|
-
return
|
|
15178
|
+
return jsonResponse16({ error: "Missing required fields: username, password" }, 400);
|
|
15202
15179
|
}
|
|
15203
15180
|
try {
|
|
15204
15181
|
const result = await signUp({
|
|
@@ -15206,21 +15183,21 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15206
15183
|
password: b.password,
|
|
15207
15184
|
name: typeof b.name === "string" ? b.name : void 0
|
|
15208
15185
|
});
|
|
15209
|
-
return
|
|
15186
|
+
return jsonResponse16(result, 201);
|
|
15210
15187
|
} catch (err2) {
|
|
15211
|
-
return
|
|
15188
|
+
return jsonResponse16({ error: err2 instanceof Error ? err2.message : "Sign-up failed" }, 400);
|
|
15212
15189
|
}
|
|
15213
15190
|
}
|
|
15214
15191
|
if (pathname === "/auth/username/sign-in") {
|
|
15215
15192
|
if (typeof b.username !== "string" || typeof b.password !== "string") {
|
|
15216
|
-
return
|
|
15193
|
+
return jsonResponse16({ error: "Missing required fields: username, password" }, 400);
|
|
15217
15194
|
}
|
|
15218
15195
|
try {
|
|
15219
15196
|
const result = await signIn({ username: b.username, password: b.password });
|
|
15220
|
-
return
|
|
15197
|
+
return jsonResponse16(result);
|
|
15221
15198
|
} catch (err2) {
|
|
15222
15199
|
if (err2 instanceof Error && err2.message === "Password reset required") {
|
|
15223
|
-
return
|
|
15200
|
+
return jsonResponse16(
|
|
15224
15201
|
{
|
|
15225
15202
|
error: {
|
|
15226
15203
|
code: "PASSWORD_RESET_REQUIRED",
|
|
@@ -15230,21 +15207,21 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15230
15207
|
403
|
|
15231
15208
|
);
|
|
15232
15209
|
}
|
|
15233
|
-
return
|
|
15210
|
+
return jsonResponse16({ error: "Invalid username or password" }, 401);
|
|
15234
15211
|
}
|
|
15235
15212
|
}
|
|
15236
15213
|
if (pathname === "/auth/username/change-password") {
|
|
15237
15214
|
if (typeof b.userId !== "string" || typeof b.current !== "string" || typeof b.newPassword !== "string") {
|
|
15238
|
-
return
|
|
15215
|
+
return jsonResponse16(
|
|
15239
15216
|
{ error: "Missing required fields: userId, current, newPassword" },
|
|
15240
15217
|
400
|
|
15241
15218
|
);
|
|
15242
15219
|
}
|
|
15243
15220
|
try {
|
|
15244
15221
|
const result = await changePassword(b.userId, b.current, b.newPassword);
|
|
15245
|
-
return
|
|
15222
|
+
return jsonResponse16(result);
|
|
15246
15223
|
} catch (err2) {
|
|
15247
|
-
return
|
|
15224
|
+
return jsonResponse16(
|
|
15248
15225
|
{ error: err2 instanceof Error ? err2.message : "Change password failed" },
|
|
15249
15226
|
400
|
|
15250
15227
|
);
|
|
@@ -15252,13 +15229,13 @@ function createUsernameAuthModule(config, db, sessionManager) {
|
|
|
15252
15229
|
}
|
|
15253
15230
|
if (pathname === "/auth/username/change-username") {
|
|
15254
15231
|
if (typeof b.userId !== "string" || typeof b.newUsername !== "string") {
|
|
15255
|
-
return
|
|
15232
|
+
return jsonResponse16({ error: "Missing required fields: userId, newUsername" }, 400);
|
|
15256
15233
|
}
|
|
15257
15234
|
try {
|
|
15258
15235
|
const result = await changeUsername(b.userId, b.newUsername);
|
|
15259
|
-
return
|
|
15236
|
+
return jsonResponse16(result);
|
|
15260
15237
|
} catch (err2) {
|
|
15261
|
-
return
|
|
15238
|
+
return jsonResponse16(
|
|
15262
15239
|
{ error: err2 instanceof Error ? err2.message : "Change username failed" },
|
|
15263
15240
|
400
|
|
15264
15241
|
);
|
|
@@ -15440,7 +15417,7 @@ function buildStatements(provider) {
|
|
|
15440
15417
|
const isMysql = provider === "mysql";
|
|
15441
15418
|
const ts = isPostgres ? "TIMESTAMPTZ" : isMysql ? "DATETIME(3)" : "INTEGER";
|
|
15442
15419
|
const tsNull = ts;
|
|
15443
|
-
const
|
|
15420
|
+
const json2 = isPostgres ? "JSONB" : isMysql ? "JSON" : "TEXT";
|
|
15444
15421
|
const bool = isPostgres ? "BOOLEAN" : isMysql ? "TINYINT(1)" : "INTEGER";
|
|
15445
15422
|
const ifne = "IF NOT EXISTS";
|
|
15446
15423
|
return [
|
|
@@ -15454,7 +15431,7 @@ function buildStatements(provider) {
|
|
|
15454
15431
|
name TEXT,
|
|
15455
15432
|
external_id TEXT,
|
|
15456
15433
|
external_provider TEXT,
|
|
15457
|
-
metadata ${
|
|
15434
|
+
metadata ${json2},
|
|
15458
15435
|
banned ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15459
15436
|
ban_reason TEXT,
|
|
15460
15437
|
ban_expires_at ${tsNull},
|
|
@@ -15482,7 +15459,7 @@ function buildStatements(provider) {
|
|
|
15482
15459
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15483
15460
|
name TEXT NOT NULL,
|
|
15484
15461
|
slug TEXT NOT NULL UNIQUE,
|
|
15485
|
-
settings ${
|
|
15462
|
+
settings ${json2},
|
|
15486
15463
|
status TEXT NOT NULL DEFAULT 'active',
|
|
15487
15464
|
created_at ${ts} NOT NULL,
|
|
15488
15465
|
updated_at ${ts} NOT NULL
|
|
@@ -15501,7 +15478,7 @@ function buildStatements(provider) {
|
|
|
15501
15478
|
token_prefix TEXT NOT NULL,
|
|
15502
15479
|
expires_at ${tsNull},
|
|
15503
15480
|
last_active_at ${tsNull},
|
|
15504
|
-
metadata ${
|
|
15481
|
+
metadata ${json2},
|
|
15505
15482
|
created_at ${ts} NOT NULL,
|
|
15506
15483
|
updated_at ${ts} NOT NULL
|
|
15507
15484
|
)`,
|
|
@@ -15512,8 +15489,8 @@ function buildStatements(provider) {
|
|
|
15512
15489
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15513
15490
|
agent_id TEXT NOT NULL REFERENCES kavach_agents(id) ON DELETE CASCADE,
|
|
15514
15491
|
resource TEXT NOT NULL,
|
|
15515
|
-
actions ${
|
|
15516
|
-
constraints ${
|
|
15492
|
+
actions ${json2} NOT NULL,
|
|
15493
|
+
constraints ${json2},
|
|
15517
15494
|
created_at ${ts} NOT NULL
|
|
15518
15495
|
)`,
|
|
15519
15496
|
// ------------------------------------------------------------------
|
|
@@ -15523,7 +15500,7 @@ function buildStatements(provider) {
|
|
|
15523
15500
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15524
15501
|
from_agent_id TEXT NOT NULL REFERENCES kavach_agents(id),
|
|
15525
15502
|
to_agent_id TEXT NOT NULL REFERENCES kavach_agents(id),
|
|
15526
|
-
permissions ${
|
|
15503
|
+
permissions ${json2} NOT NULL,
|
|
15527
15504
|
depth INTEGER NOT NULL DEFAULT 1,
|
|
15528
15505
|
max_depth INTEGER NOT NULL DEFAULT 3,
|
|
15529
15506
|
status TEXT NOT NULL DEFAULT 'active',
|
|
@@ -15539,7 +15516,7 @@ function buildStatements(provider) {
|
|
|
15539
15516
|
user_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15540
15517
|
action TEXT NOT NULL,
|
|
15541
15518
|
resource TEXT NOT NULL,
|
|
15542
|
-
parameters ${
|
|
15519
|
+
parameters ${json2},
|
|
15543
15520
|
result TEXT NOT NULL,
|
|
15544
15521
|
reason TEXT,
|
|
15545
15522
|
duration_ms INTEGER NOT NULL,
|
|
@@ -15565,7 +15542,7 @@ function buildStatements(provider) {
|
|
|
15565
15542
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15566
15543
|
name TEXT NOT NULL,
|
|
15567
15544
|
endpoint TEXT NOT NULL UNIQUE,
|
|
15568
|
-
tools ${
|
|
15545
|
+
tools ${json2} NOT NULL,
|
|
15569
15546
|
auth_required ${bool} NOT NULL DEFAULT ${isPostgres ? "TRUE" : "1"},
|
|
15570
15547
|
rate_limit_rpm INTEGER,
|
|
15571
15548
|
status TEXT NOT NULL DEFAULT 'active',
|
|
@@ -15579,7 +15556,7 @@ function buildStatements(provider) {
|
|
|
15579
15556
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15580
15557
|
user_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15581
15558
|
expires_at ${ts} NOT NULL,
|
|
15582
|
-
metadata ${
|
|
15559
|
+
metadata ${json2},
|
|
15583
15560
|
created_at ${ts} NOT NULL
|
|
15584
15561
|
)`,
|
|
15585
15562
|
// ------------------------------------------------------------------
|
|
@@ -15591,13 +15568,13 @@ function buildStatements(provider) {
|
|
|
15591
15568
|
client_secret TEXT,
|
|
15592
15569
|
client_name TEXT,
|
|
15593
15570
|
client_uri TEXT,
|
|
15594
|
-
redirect_uris ${
|
|
15595
|
-
grant_types ${
|
|
15596
|
-
response_types ${
|
|
15571
|
+
redirect_uris ${json2} NOT NULL,
|
|
15572
|
+
grant_types ${json2} NOT NULL,
|
|
15573
|
+
response_types ${json2} NOT NULL,
|
|
15597
15574
|
token_endpoint_auth_method TEXT NOT NULL DEFAULT 'client_secret_basic',
|
|
15598
15575
|
type TEXT NOT NULL DEFAULT 'confidential',
|
|
15599
15576
|
disabled ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15600
|
-
metadata ${
|
|
15577
|
+
metadata ${json2},
|
|
15601
15578
|
created_at ${ts} NOT NULL,
|
|
15602
15579
|
updated_at ${ts} NOT NULL
|
|
15603
15580
|
)`,
|
|
@@ -15665,8 +15642,8 @@ function buildStatements(provider) {
|
|
|
15665
15642
|
agent_id TEXT REFERENCES kavach_agents(id) ON DELETE CASCADE,
|
|
15666
15643
|
user_id TEXT REFERENCES kavach_users(id),
|
|
15667
15644
|
tenant_id TEXT REFERENCES kavach_tenants(id),
|
|
15668
|
-
limits ${
|
|
15669
|
-
current_usage ${
|
|
15645
|
+
limits ${json2} NOT NULL,
|
|
15646
|
+
current_usage ${json2} NOT NULL,
|
|
15670
15647
|
action TEXT NOT NULL DEFAULT 'warn',
|
|
15671
15648
|
status TEXT NOT NULL DEFAULT 'active',
|
|
15672
15649
|
created_at ${ts} NOT NULL
|
|
@@ -15680,11 +15657,11 @@ function buildStatements(provider) {
|
|
|
15680
15657
|
name TEXT NOT NULL,
|
|
15681
15658
|
description TEXT,
|
|
15682
15659
|
version TEXT NOT NULL,
|
|
15683
|
-
protocols ${
|
|
15684
|
-
capabilities ${
|
|
15685
|
-
auth_requirements ${
|
|
15660
|
+
protocols ${json2} NOT NULL,
|
|
15661
|
+
capabilities ${json2} NOT NULL,
|
|
15662
|
+
auth_requirements ${json2} NOT NULL,
|
|
15686
15663
|
endpoint TEXT,
|
|
15687
|
-
metadata ${
|
|
15664
|
+
metadata ${json2},
|
|
15688
15665
|
created_at ${ts} NOT NULL,
|
|
15689
15666
|
updated_at ${ts} NOT NULL
|
|
15690
15667
|
)`,
|
|
@@ -15697,7 +15674,7 @@ function buildStatements(provider) {
|
|
|
15697
15674
|
user_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15698
15675
|
action TEXT NOT NULL,
|
|
15699
15676
|
resource TEXT NOT NULL,
|
|
15700
|
-
arguments ${
|
|
15677
|
+
arguments ${json2},
|
|
15701
15678
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
15702
15679
|
expires_at ${ts} NOT NULL,
|
|
15703
15680
|
responded_at ${tsNull},
|
|
@@ -15711,7 +15688,7 @@ function buildStatements(provider) {
|
|
|
15711
15688
|
agent_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_agents(id) ON DELETE CASCADE,
|
|
15712
15689
|
score INTEGER NOT NULL,
|
|
15713
15690
|
level TEXT NOT NULL,
|
|
15714
|
-
factors ${
|
|
15691
|
+
factors ${json2} NOT NULL,
|
|
15715
15692
|
computed_at ${ts} NOT NULL
|
|
15716
15693
|
)`,
|
|
15717
15694
|
// ------------------------------------------------------------------
|
|
@@ -15722,7 +15699,7 @@ function buildStatements(provider) {
|
|
|
15722
15699
|
name TEXT NOT NULL,
|
|
15723
15700
|
slug TEXT NOT NULL UNIQUE,
|
|
15724
15701
|
owner_id TEXT NOT NULL REFERENCES kavach_users(id),
|
|
15725
|
-
metadata ${
|
|
15702
|
+
metadata ${json2},
|
|
15726
15703
|
created_at ${ts} NOT NULL,
|
|
15727
15704
|
updated_at ${ts} NOT NULL
|
|
15728
15705
|
)`,
|
|
@@ -15757,7 +15734,7 @@ function buildStatements(provider) {
|
|
|
15757
15734
|
id TEXT NOT NULL PRIMARY KEY,
|
|
15758
15735
|
org_id TEXT NOT NULL REFERENCES kavach_organizations(id) ON DELETE CASCADE,
|
|
15759
15736
|
name TEXT NOT NULL,
|
|
15760
|
-
permissions ${
|
|
15737
|
+
permissions ${json2} NOT NULL,
|
|
15761
15738
|
UNIQUE(org_id, name)
|
|
15762
15739
|
)`,
|
|
15763
15740
|
// ------------------------------------------------------------------
|
|
@@ -15793,7 +15770,7 @@ function buildStatements(provider) {
|
|
|
15793
15770
|
token_hash TEXT NOT NULL UNIQUE,
|
|
15794
15771
|
purpose TEXT NOT NULL,
|
|
15795
15772
|
identifier TEXT NOT NULL,
|
|
15796
|
-
metadata ${
|
|
15773
|
+
metadata ${json2},
|
|
15797
15774
|
used ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15798
15775
|
expires_at ${ts} NOT NULL,
|
|
15799
15776
|
created_at ${ts} NOT NULL
|
|
@@ -15838,7 +15815,7 @@ function buildStatements(provider) {
|
|
|
15838
15815
|
user_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_users(id),
|
|
15839
15816
|
secret TEXT NOT NULL,
|
|
15840
15817
|
enabled ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
|
|
15841
|
-
backup_codes ${
|
|
15818
|
+
backup_codes ${json2} NOT NULL,
|
|
15842
15819
|
created_at ${ts} NOT NULL,
|
|
15843
15820
|
updated_at ${ts} NOT NULL
|
|
15844
15821
|
)`,
|
|
@@ -15863,7 +15840,7 @@ function buildStatements(provider) {
|
|
|
15863
15840
|
name TEXT NOT NULL,
|
|
15864
15841
|
key_hash TEXT NOT NULL,
|
|
15865
15842
|
key_prefix TEXT NOT NULL,
|
|
15866
|
-
permissions ${
|
|
15843
|
+
permissions ${json2} NOT NULL,
|
|
15867
15844
|
expires_at ${tsNull},
|
|
15868
15845
|
last_used_at ${tsNull},
|
|
15869
15846
|
created_at ${ts} NOT NULL
|
|
@@ -15922,10 +15899,10 @@ function buildStatements(provider) {
|
|
|
15922
15899
|
client_id TEXT NOT NULL UNIQUE,
|
|
15923
15900
|
client_secret_hash TEXT NOT NULL,
|
|
15924
15901
|
client_name TEXT NOT NULL,
|
|
15925
|
-
redirect_uris ${
|
|
15926
|
-
grant_types ${
|
|
15927
|
-
response_types ${
|
|
15928
|
-
scopes ${
|
|
15902
|
+
redirect_uris ${json2} NOT NULL,
|
|
15903
|
+
grant_types ${json2} NOT NULL,
|
|
15904
|
+
response_types ${json2} NOT NULL,
|
|
15905
|
+
scopes ${json2} NOT NULL,
|
|
15929
15906
|
token_endpoint_auth_method TEXT NOT NULL DEFAULT 'client_secret_post',
|
|
15930
15907
|
created_at ${ts} NOT NULL,
|
|
15931
15908
|
updated_at ${ts} NOT NULL
|
|
@@ -15971,7 +15948,7 @@ function buildStatements(provider) {
|
|
|
15971
15948
|
output_tokens INTEGER,
|
|
15972
15949
|
cost_micros INTEGER NOT NULL,
|
|
15973
15950
|
currency TEXT NOT NULL DEFAULT 'USD',
|
|
15974
|
-
metadata ${
|
|
15951
|
+
metadata ${json2},
|
|
15975
15952
|
delegation_chain_id TEXT,
|
|
15976
15953
|
recorded_at ${ts} NOT NULL
|
|
15977
15954
|
)`,
|
|
@@ -16019,7 +15996,7 @@ function buildStatements(provider) {
|
|
|
16019
15996
|
id TEXT NOT NULL PRIMARY KEY,
|
|
16020
15997
|
type TEXT NOT NULL,
|
|
16021
15998
|
timestamp ${ts} NOT NULL,
|
|
16022
|
-
data ${
|
|
15999
|
+
data ${json2} NOT NULL,
|
|
16023
16000
|
agent_id TEXT,
|
|
16024
16001
|
user_id TEXT
|
|
16025
16002
|
)`,
|
|
@@ -16080,7 +16057,7 @@ function buildStatements(provider) {
|
|
|
16080
16057
|
source_instance_id TEXT NOT NULL,
|
|
16081
16058
|
target_instance_id TEXT,
|
|
16082
16059
|
direction TEXT NOT NULL,
|
|
16083
|
-
permissions ${
|
|
16060
|
+
permissions ${json2} NOT NULL,
|
|
16084
16061
|
trust_score INTEGER,
|
|
16085
16062
|
expires_at ${ts} NOT NULL,
|
|
16086
16063
|
created_at ${ts} NOT NULL
|
|
@@ -17398,6 +17375,18 @@ function matchPath(pattern, pathname) {
|
|
|
17398
17375
|
return params;
|
|
17399
17376
|
}
|
|
17400
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
|
+
}
|
|
17401
17390
|
return {
|
|
17402
17391
|
async handle(request, basePath, endpointCtx) {
|
|
17403
17392
|
const url = new URL(request.url);
|
|
@@ -17417,6 +17406,20 @@ function createPluginRouter(endpoints) {
|
|
|
17417
17406
|
if (endpoint.method !== method) continue;
|
|
17418
17407
|
const params = matchPath(endpoint.path, pathname);
|
|
17419
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
|
+
}
|
|
17420
17423
|
const enrichedUrl = new URL(request.url);
|
|
17421
17424
|
for (const [key, value] of Object.entries(params)) {
|
|
17422
17425
|
enrichedUrl.searchParams.set(`_param_${key}`, value);
|
|
@@ -17474,7 +17477,7 @@ async function runMigrations(db, provider, statements) {
|
|
|
17474
17477
|
}
|
|
17475
17478
|
throw new Error(`runMigrations: unsupported provider "${provider}"`);
|
|
17476
17479
|
}
|
|
17477
|
-
async function initializePlugins(plugins, db, config) {
|
|
17480
|
+
async function initializePlugins(plugins, db, config, sessionManager) {
|
|
17478
17481
|
const registry = {
|
|
17479
17482
|
endpoints: [],
|
|
17480
17483
|
migrations: [],
|
|
@@ -17491,6 +17494,7 @@ async function initializePlugins(plugins, db, config) {
|
|
|
17491
17494
|
const ctx = {
|
|
17492
17495
|
db,
|
|
17493
17496
|
config,
|
|
17497
|
+
sessionManager,
|
|
17494
17498
|
addEndpoint(endpoint) {
|
|
17495
17499
|
registry.endpoints.push(endpoint);
|
|
17496
17500
|
},
|
|
@@ -17731,8 +17735,8 @@ var DEFAULT_EXCLUDE_PATHS = [
|
|
|
17731
17735
|
var encoder = new TextEncoder();
|
|
17732
17736
|
var decoder = new TextDecoder();
|
|
17733
17737
|
function encodeState2(state) {
|
|
17734
|
-
const
|
|
17735
|
-
const bytes = encoder.encode(
|
|
17738
|
+
const json2 = JSON.stringify(state);
|
|
17739
|
+
const bytes = encoder.encode(json2);
|
|
17736
17740
|
let binary = "";
|
|
17737
17741
|
for (let i = 0; i < bytes.length; i++) {
|
|
17738
17742
|
binary += String.fromCharCode(bytes[i]);
|
|
@@ -17750,8 +17754,8 @@ function decodeState(encoded) {
|
|
|
17750
17754
|
for (let i = 0; i < binary.length; i++) {
|
|
17751
17755
|
bytes[i] = binary.charCodeAt(i);
|
|
17752
17756
|
}
|
|
17753
|
-
const
|
|
17754
|
-
const parsed = JSON.parse(
|
|
17757
|
+
const json2 = decoder.decode(bytes);
|
|
17758
|
+
const parsed = JSON.parse(json2);
|
|
17755
17759
|
if (!isValidChainState(parsed)) return null;
|
|
17756
17760
|
return parsed;
|
|
17757
17761
|
} catch {
|
|
@@ -17823,7 +17827,7 @@ function createRedirectChain(config) {
|
|
|
17823
17827
|
if (cookieOpts.domain) parts.push(`Domain=${cookieOpts.domain}`);
|
|
17824
17828
|
return parts.join("; ");
|
|
17825
17829
|
}
|
|
17826
|
-
function
|
|
17830
|
+
function buildClearCookie2() {
|
|
17827
17831
|
return buildCookieHeader("", 0);
|
|
17828
17832
|
}
|
|
17829
17833
|
function serializeAndSetCookie(state) {
|
|
@@ -17933,7 +17937,7 @@ function createRedirectChain(config) {
|
|
|
17933
17937
|
pop(request) {
|
|
17934
17938
|
const state = parseFromRequest(request);
|
|
17935
17939
|
if (!state) {
|
|
17936
|
-
return { url: defaultPath, done: true, clearCookie:
|
|
17940
|
+
return { url: defaultPath, done: true, clearCookie: buildClearCookie2() };
|
|
17937
17941
|
}
|
|
17938
17942
|
if (state.steps.length > 0) {
|
|
17939
17943
|
const next = state.steps.shift();
|
|
@@ -17957,7 +17961,7 @@ function createRedirectChain(config) {
|
|
|
17957
17961
|
return {
|
|
17958
17962
|
url,
|
|
17959
17963
|
done: true,
|
|
17960
|
-
clearCookie:
|
|
17964
|
+
clearCookie: buildClearCookie2()
|
|
17961
17965
|
};
|
|
17962
17966
|
},
|
|
17963
17967
|
peek(request) {
|
|
@@ -17983,7 +17987,7 @@ function createRedirectChain(config) {
|
|
|
17983
17987
|
},
|
|
17984
17988
|
clear() {
|
|
17985
17989
|
currentState = null;
|
|
17986
|
-
return
|
|
17990
|
+
return buildClearCookie2();
|
|
17987
17991
|
},
|
|
17988
17992
|
parse(request) {
|
|
17989
17993
|
return parseFromRequest(request);
|
|
@@ -18316,12 +18320,33 @@ async function createKavach(config) {
|
|
|
18316
18320
|
const captchaModule = config.captcha ? createCaptchaModule(config.captcha) : null;
|
|
18317
18321
|
const redirectChain = createRedirectChain(config.redirects);
|
|
18318
18322
|
const webhookModule = config.webhooks && config.webhooks.length > 0 ? createWebhookModule(config.webhooks) : null;
|
|
18319
|
-
const pluginRegistry = await initializePlugins(config.plugins ?? [], db, config);
|
|
18323
|
+
const pluginRegistry = await initializePlugins(config.plugins ?? [], db, config, sessionManager);
|
|
18320
18324
|
const endpointCtx = {
|
|
18321
18325
|
db,
|
|
18322
18326
|
async getUser(request) {
|
|
18323
|
-
if (
|
|
18324
|
-
|
|
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;
|
|
18325
18350
|
},
|
|
18326
18351
|
async getSession(token) {
|
|
18327
18352
|
if (!sessionManager) return null;
|
|
@@ -18329,6 +18354,46 @@ async function createKavach(config) {
|
|
|
18329
18354
|
}
|
|
18330
18355
|
};
|
|
18331
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
|
+
}
|
|
18332
18397
|
async function authorize(agentId, request, context) {
|
|
18333
18398
|
if (hooks.beforeAuthorize) {
|
|
18334
18399
|
const verdict = await hooks.beforeAuthorize({
|
|
@@ -19400,7 +19465,7 @@ function parseCookies2(header) {
|
|
|
19400
19465
|
}
|
|
19401
19466
|
return result;
|
|
19402
19467
|
}
|
|
19403
|
-
function
|
|
19468
|
+
function getCookie2(header, name) {
|
|
19404
19469
|
return parseCookies2(header)[name];
|
|
19405
19470
|
}
|
|
19406
19471
|
function parseCookiesFromRequest(request) {
|
|
@@ -19521,7 +19586,7 @@ function createCookieSessionManager(config, db) {
|
|
|
19521
19586
|
...config.cookieOptions,
|
|
19522
19587
|
maxAge: maxAgeSecs
|
|
19523
19588
|
};
|
|
19524
|
-
function
|
|
19589
|
+
function buildSetCookie2(token) {
|
|
19525
19590
|
return serializeCookie(sessionName, token, baseCookieOpts);
|
|
19526
19591
|
}
|
|
19527
19592
|
function buildDeleteCookie() {
|
|
@@ -19530,10 +19595,10 @@ function createCookieSessionManager(config, db) {
|
|
|
19530
19595
|
}
|
|
19531
19596
|
async function createSession(userId, metadata) {
|
|
19532
19597
|
const { session, token } = await raw.create(userId, metadata);
|
|
19533
|
-
return { session, setCookieHeader:
|
|
19598
|
+
return { session, setCookieHeader: buildSetCookie2(token) };
|
|
19534
19599
|
}
|
|
19535
19600
|
async function validateSession(cookieHeader) {
|
|
19536
|
-
const token =
|
|
19601
|
+
const token = getCookie2(cookieHeader, sessionName);
|
|
19537
19602
|
if (!token) {
|
|
19538
19603
|
return { session: null, refreshCookieHeader: null };
|
|
19539
19604
|
}
|
|
@@ -19559,7 +19624,7 @@ function createCookieSessionManager(config, db) {
|
|
|
19559
19624
|
row.userId,
|
|
19560
19625
|
row.metadata ?? void 0
|
|
19561
19626
|
);
|
|
19562
|
-
return { session: newSession, setCookieHeader:
|
|
19627
|
+
return { session: newSession, setCookieHeader: buildSetCookie2(newToken) };
|
|
19563
19628
|
}
|
|
19564
19629
|
async function revokeSession(sessionId) {
|
|
19565
19630
|
await raw.revoke(sessionId);
|
|
@@ -19903,7 +19968,7 @@ function createSessionRefresher(config) {
|
|
|
19903
19968
|
}
|
|
19904
19969
|
async function handleRequest(request) {
|
|
19905
19970
|
const cookieHeader = request.headers.get("cookie") ?? "";
|
|
19906
|
-
let rawToken =
|
|
19971
|
+
let rawToken = getCookie2(cookieHeader, refreshCookieName);
|
|
19907
19972
|
if (!rawToken) {
|
|
19908
19973
|
try {
|
|
19909
19974
|
const body = await request.clone().json();
|
|
@@ -20634,6 +20699,6 @@ async function verifyWebhookSignature3(secret, rawBody, signature) {
|
|
|
20634
20699
|
return diff === 0;
|
|
20635
20700
|
}
|
|
20636
20701
|
|
|
20637
|
-
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 };
|
|
20638
20703
|
//# sourceMappingURL=index.js.map
|
|
20639
20704
|
//# sourceMappingURL=index.js.map
|