@iqauth/sdk 2.0.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/LICENSE +21 -0
- package/README.md +287 -0
- package/dist/browser-session.d.mts +12 -0
- package/dist/browser-session.d.ts +12 -0
- package/dist/browser-session.js +1812 -0
- package/dist/browser-session.mjs +28 -0
- package/dist/browser.d.mts +46 -0
- package/dist/browser.d.ts +46 -0
- package/dist/browser.js +768 -0
- package/dist/browser.mjs +47 -0
- package/dist/chunk-5HF3OBNO.mjs +189 -0
- package/dist/chunk-5WFR6Y33.mjs +59 -0
- package/dist/chunk-6I6RM4MN.mjs +51 -0
- package/dist/chunk-73R6BEGO.mjs +176 -0
- package/dist/chunk-E46DKOVI.mjs +632 -0
- package/dist/chunk-JQWYIIIS.mjs +1740 -0
- package/dist/chunk-X3K3WOBR.mjs +64 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +581 -0
- package/dist/cli/index.mjs +57 -0
- package/dist/client-C1DXfB8Z.d.mts +911 -0
- package/dist/client-CggvJmmm.d.ts +911 -0
- package/dist/dev-FUTJZSWN.mjs +56 -0
- package/dist/doctor-OHJRZBBT.mjs +89 -0
- package/dist/errors-CDdl24MP.d.mts +52 -0
- package/dist/errors-CDdl24MP.d.ts +52 -0
- package/dist/express-BKAXB5Nl.d.ts +61 -0
- package/dist/express-CpfyYTmw.d.mts +61 -0
- package/dist/express.d.mts +45 -0
- package/dist/express.d.ts +45 -0
- package/dist/express.js +2252 -0
- package/dist/express.mjs +122 -0
- package/dist/fastify.d.mts +23 -0
- package/dist/fastify.d.ts +23 -0
- package/dist/fastify.js +2062 -0
- package/dist/fastify.mjs +118 -0
- package/dist/hono.d.mts +22 -0
- package/dist/hono.d.ts +22 -0
- package/dist/hono.js +2051 -0
- package/dist/hono.mjs +107 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +2070 -0
- package/dist/index.mjs +83 -0
- package/dist/init-LLCSQGNL.mjs +198 -0
- package/dist/keys-NLWFAOEM.mjs +63 -0
- package/dist/mobile.d.mts +11 -0
- package/dist/mobile.d.ts +11 -0
- package/dist/mobile.js +1809 -0
- package/dist/mobile.mjs +25 -0
- package/dist/next.d.mts +37 -0
- package/dist/next.d.ts +37 -0
- package/dist/next.js +2078 -0
- package/dist/next.mjs +130 -0
- package/dist/publishableKey-B5DIK81A.d.mts +24 -0
- package/dist/publishableKey-B5DIK81A.d.ts +24 -0
- package/dist/react.d.mts +196 -0
- package/dist/react.d.ts +196 -0
- package/dist/react.js +1457 -0
- package/dist/react.mjs +787 -0
- package/dist/server/handlers.d.mts +96 -0
- package/dist/server/handlers.d.ts +96 -0
- package/dist/server/handlers.js +243 -0
- package/dist/server/handlers.mjs +14 -0
- package/dist/server.d.mts +14 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.js +2195 -0
- package/dist/server.mjs +47 -0
- package/dist/service.d.mts +11 -0
- package/dist/service.d.ts +11 -0
- package/dist/service.js +1809 -0
- package/dist/service.mjs +25 -0
- package/dist/signIn-C8f6qVjD.d.mts +238 -0
- package/dist/signIn-Cy2lbEXb.d.ts +238 -0
- package/dist/types-Cxl3bQHt.d.mts +900 -0
- package/dist/types-Cxl3bQHt.d.ts +900 -0
- package/docs/APP_INTEGRATION_MATRIX.md +59 -0
- package/docs/BROWSER_SESSION_MIGRATION.md +69 -0
- package/docs/FRESH_IMPLEMENTATION_GUIDE.md +188 -0
- package/docs/TARBALL_RELEASE_WORKFLOW.md +98 -0
- package/docs/V1_TO_V2_UPGRADE_GUIDE.md +318 -0
- package/docs/guides/api-keys.md +130 -0
- package/docs/guides/app-registration.md +149 -0
- package/docs/guides/auth-flows.md +168 -0
- package/docs/guides/branding.md +160 -0
- package/docs/guides/entitlements.md +115 -0
- package/docs/guides/entity-hierarchy.md +200 -0
- package/docs/guides/error-handling.md +251 -0
- package/docs/guides/gdpr-compliance.md +123 -0
- package/docs/guides/invitations.md +143 -0
- package/docs/guides/mfa-enrollment.md +170 -0
- package/docs/guides/middleware-reference.md +205 -0
- package/docs/guides/mobile-native.md +110 -0
- package/docs/guides/roles-and-permissions.md +220 -0
- package/docs/guides/scoped-authorization.md +247 -0
- package/docs/guides/server-platform-integration.md +52 -0
- package/docs/guides/service-automation-integration.md +36 -0
- package/docs/guides/session-management.md +97 -0
- package/docs/guides/tenant-management.md +216 -0
- package/docs/guides/token-verification.md +178 -0
- package/docs/guides/user-management.md +184 -0
- package/docs/guides/webhooks.md +136 -0
- package/docs/integration-prompts/README.md +20 -0
- package/docs/integration-prompts/first-party-browser-app.md +29 -0
- package/docs/integration-prompts/install-from-tarball.md +41 -0
- package/docs/integration-prompts/migrate-from-local-packages-source.md +57 -0
- package/docs/integration-prompts/native-mobile-app.md +24 -0
- package/docs/integration-prompts/server-platform-app.md +20 -0
- package/docs/integration-prompts/service-automation-app.md +20 -0
- package/package.json +115 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/cli/util.ts
|
|
2
|
+
import { readFile, access } from "fs/promises";
|
|
3
|
+
function parseFlags(argv) {
|
|
4
|
+
const flags = /* @__PURE__ */ new Map();
|
|
5
|
+
const bools = /* @__PURE__ */ new Set();
|
|
6
|
+
const positional = [];
|
|
7
|
+
for (let i = 0; i < argv.length; i++) {
|
|
8
|
+
const arg = argv[i];
|
|
9
|
+
if (arg.startsWith("--")) {
|
|
10
|
+
const key = arg.slice(2);
|
|
11
|
+
const next = argv[i + 1];
|
|
12
|
+
if (next && !next.startsWith("--")) {
|
|
13
|
+
flags.set(key, next);
|
|
14
|
+
i++;
|
|
15
|
+
} else {
|
|
16
|
+
bools.add(key);
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
positional.push(arg);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { flags, bools, positional };
|
|
23
|
+
}
|
|
24
|
+
async function loadEnv(path = ".env") {
|
|
25
|
+
try {
|
|
26
|
+
await access(path);
|
|
27
|
+
} catch {
|
|
28
|
+
return { ...process.env };
|
|
29
|
+
}
|
|
30
|
+
const raw = await readFile(path, "utf8");
|
|
31
|
+
const out = { ...process.env };
|
|
32
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
33
|
+
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
34
|
+
if (!m) continue;
|
|
35
|
+
let val = m[2];
|
|
36
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
37
|
+
val = val.slice(1, -1);
|
|
38
|
+
}
|
|
39
|
+
if (out[m[1]] === void 0) out[m[1]] = val;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
async function callApi(baseUrl, path, init = {}) {
|
|
44
|
+
const headers = { "Content-Type": "application/json", ...init.headers };
|
|
45
|
+
if (init.token) headers["Authorization"] = `Bearer ${init.token}`;
|
|
46
|
+
const res = await fetch(`${baseUrl.replace(/\/+$/, "")}${path}`, { ...init, headers });
|
|
47
|
+
const json = await res.json().catch(() => ({}));
|
|
48
|
+
if (!res.ok || json.success === false) {
|
|
49
|
+
const code = json.error?.code || res.status;
|
|
50
|
+
const msg = json.error?.message || res.statusText;
|
|
51
|
+
throw new Error(`${path} \u2192 ${code}: ${msg}`);
|
|
52
|
+
}
|
|
53
|
+
return json.data ?? json;
|
|
54
|
+
}
|
|
55
|
+
function symbol(ok) {
|
|
56
|
+
return ok ? "\u2713" : "\u2717";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
parseFlags,
|
|
61
|
+
loadEnv,
|
|
62
|
+
callApi,
|
|
63
|
+
symbol
|
|
64
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/cli/init.ts
|
|
14
|
+
var init_exports = {};
|
|
15
|
+
function parseArgs(argv) {
|
|
16
|
+
const get = (key, def) => {
|
|
17
|
+
const idx = argv.indexOf(`--${key}`);
|
|
18
|
+
if (idx >= 0 && argv[idx + 1] && !argv[idx + 1].startsWith("--")) return argv[idx + 1];
|
|
19
|
+
return def;
|
|
20
|
+
};
|
|
21
|
+
const has = (key) => argv.includes(`--${key}`);
|
|
22
|
+
const baseUrl = get("base-url") || process.env.IQAUTH_BASE_URL;
|
|
23
|
+
const email = get("email");
|
|
24
|
+
const password = get("password");
|
|
25
|
+
const appName = get("app");
|
|
26
|
+
const redirect = get("redirect");
|
|
27
|
+
const origin = get("origin");
|
|
28
|
+
if (!baseUrl || !email || !password || !appName || !redirect || !origin) {
|
|
29
|
+
console.error("Usage: iqauth init --base-url URL --email EMAIL --password PW --app NAME --redirect URL --origin URL [--org NAME] [--mode test|live] [--env-file .env] [--rotate] [--yes]");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
baseUrl: baseUrl.replace(/\/$/, ""),
|
|
34
|
+
email,
|
|
35
|
+
password,
|
|
36
|
+
org: get("org"),
|
|
37
|
+
appName,
|
|
38
|
+
redirect,
|
|
39
|
+
origin,
|
|
40
|
+
mode: get("mode", "test") || "test",
|
|
41
|
+
envFile: get("env-file", ".env"),
|
|
42
|
+
rotate: has("rotate"),
|
|
43
|
+
yes: has("yes") || has("y")
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function http(url, init) {
|
|
47
|
+
const headers = { "Content-Type": "application/json", ...init.headers };
|
|
48
|
+
if (init.token) headers["Authorization"] = `Bearer ${init.token}`;
|
|
49
|
+
const res = await fetch(url, { ...init, headers });
|
|
50
|
+
const json = await res.json().catch(() => ({}));
|
|
51
|
+
if (!res.ok || json?.success === false) {
|
|
52
|
+
const code = json?.error?.code || res.status;
|
|
53
|
+
const msg = json?.error?.message || res.statusText;
|
|
54
|
+
throw new Error(`${url} \u2192 ${code}: ${msg}`);
|
|
55
|
+
}
|
|
56
|
+
return json.data;
|
|
57
|
+
}
|
|
58
|
+
async function confirm(question) {
|
|
59
|
+
if (!process.stdin.isTTY) return false;
|
|
60
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
61
|
+
return new Promise((resolve2) => {
|
|
62
|
+
rl.question(`${question} [y/N] `, (answer) => {
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve2(/^y(es)?$/i.test(answer.trim()));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function mergeEnv(path, updates) {
|
|
69
|
+
let existing = "";
|
|
70
|
+
try {
|
|
71
|
+
await (0, import_promises.access)(path);
|
|
72
|
+
existing = await (0, import_promises.readFile)(path, "utf8");
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
const lines = existing.split(/\r?\n/);
|
|
76
|
+
const seen = /* @__PURE__ */ new Set();
|
|
77
|
+
const out = lines.map((line) => {
|
|
78
|
+
const m = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
79
|
+
if (!m) return line;
|
|
80
|
+
const key = m[1];
|
|
81
|
+
if (Object.prototype.hasOwnProperty.call(updates, key)) {
|
|
82
|
+
seen.add(key);
|
|
83
|
+
return `${key}=${updates[key]}`;
|
|
84
|
+
}
|
|
85
|
+
return line;
|
|
86
|
+
});
|
|
87
|
+
for (const [k, v] of Object.entries(updates)) {
|
|
88
|
+
if (!seen.has(k)) out.push(`${k}=${v}`);
|
|
89
|
+
}
|
|
90
|
+
while (out.length && out[out.length - 1] === "") out.pop();
|
|
91
|
+
await (0, import_promises.writeFile)(path, out.join("\n") + "\n", "utf8");
|
|
92
|
+
}
|
|
93
|
+
async function main() {
|
|
94
|
+
const args = parseArgs(process.argv.slice(2));
|
|
95
|
+
console.log(`\u2192 Signing in / creating account at ${args.baseUrl} ...`);
|
|
96
|
+
const signup = await http(
|
|
97
|
+
`${args.baseUrl}/api/v1/signup`,
|
|
98
|
+
{ method: "POST", body: JSON.stringify({ email: args.email, password: args.password, name: args.email.split("@")[0], organizationName: args.org }) }
|
|
99
|
+
).catch(async (err) => {
|
|
100
|
+
if (String(err.message).includes("EMAIL_IN_USE")) {
|
|
101
|
+
console.log(" account exists, signing in instead ...");
|
|
102
|
+
const login = await http(
|
|
103
|
+
`${args.baseUrl}/api/v1/auth/login`,
|
|
104
|
+
{ method: "POST", body: JSON.stringify({ email: args.email, password: args.password }) }
|
|
105
|
+
);
|
|
106
|
+
return { accessToken: login.accessToken, tenant: login.tenants?.[0] || { id: "", slug: "" }, user: login.user };
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
});
|
|
110
|
+
console.log(` \u2713 signed in as ${signup.user.email}`);
|
|
111
|
+
const existing = await http(
|
|
112
|
+
`${args.baseUrl}/api/v1/apps`,
|
|
113
|
+
{ method: "GET", token: signup.accessToken }
|
|
114
|
+
).catch(() => []);
|
|
115
|
+
const match = existing.find((a) => a.name === args.appName);
|
|
116
|
+
let appOut;
|
|
117
|
+
let clientOut;
|
|
118
|
+
let pkOut;
|
|
119
|
+
let skOut;
|
|
120
|
+
if (match) {
|
|
121
|
+
console.log(` \u26A0 app "${args.appName}" already exists (id=${match.id}, mode=${match.mode}).`);
|
|
122
|
+
if (!args.rotate) {
|
|
123
|
+
console.log(" re-using its existing keys is not possible (raw keys are only shown once).");
|
|
124
|
+
console.log(" pass --rotate to rotate the publishable + secret keys (24h grace window).");
|
|
125
|
+
process.exit(2);
|
|
126
|
+
}
|
|
127
|
+
const ok = args.yes || await confirm(" rotate publishable + secret keys for this app?");
|
|
128
|
+
if (!ok) {
|
|
129
|
+
console.log(" aborted.");
|
|
130
|
+
process.exit(2);
|
|
131
|
+
}
|
|
132
|
+
const keys = await http(
|
|
133
|
+
`${args.baseUrl}/api/v1/apps/${match.id}/keys`,
|
|
134
|
+
{ method: "GET", token: signup.accessToken }
|
|
135
|
+
);
|
|
136
|
+
const pkKey = keys.find((k) => k.kind === "publishable" && k.mode === args.mode);
|
|
137
|
+
const skKey = keys.find((k) => k.kind === "secret" && k.mode === args.mode);
|
|
138
|
+
if (!pkKey || !skKey) {
|
|
139
|
+
console.error(" no existing pk_/sk_ pair found for this mode; cannot rotate.");
|
|
140
|
+
process.exit(3);
|
|
141
|
+
}
|
|
142
|
+
const pkRot = await http(
|
|
143
|
+
`${args.baseUrl}/api/v1/apps/${match.id}/keys/${pkKey.id}/rotate`,
|
|
144
|
+
{ method: "POST", token: signup.accessToken }
|
|
145
|
+
);
|
|
146
|
+
const skRot = await http(
|
|
147
|
+
`${args.baseUrl}/api/v1/apps/${match.id}/keys/${skKey.id}/rotate`,
|
|
148
|
+
{ method: "POST", token: signup.accessToken }
|
|
149
|
+
);
|
|
150
|
+
pkOut = pkRot.rawKey;
|
|
151
|
+
skOut = skRot.rawKey;
|
|
152
|
+
appOut = match;
|
|
153
|
+
clientOut = { clientId: "" };
|
|
154
|
+
console.log(" \u2713 rotated keys (previous values valid for 24h)");
|
|
155
|
+
} else {
|
|
156
|
+
console.log(`\u2192 Creating app "${args.appName}" (${args.mode} mode) ...`);
|
|
157
|
+
const created = await http(`${args.baseUrl}/api/v1/apps`, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
token: signup.accessToken,
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
name: args.appName,
|
|
162
|
+
mode: args.mode,
|
|
163
|
+
redirectUris: [args.redirect],
|
|
164
|
+
allowedOrigins: [args.origin]
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
appOut = created.app;
|
|
168
|
+
clientOut = created.client;
|
|
169
|
+
pkOut = created.publishableKey.rawKey;
|
|
170
|
+
skOut = created.secretKey.rawKey;
|
|
171
|
+
console.log(` \u2713 app ${created.app.key} created`);
|
|
172
|
+
}
|
|
173
|
+
const updates = {
|
|
174
|
+
IQAUTH_ISSUER: args.baseUrl,
|
|
175
|
+
IQAUTH_APP_ID: appOut.id,
|
|
176
|
+
IQAUTH_APP_KEY: appOut.key,
|
|
177
|
+
IQAUTH_TENANT_ID: signup.tenant.id || "",
|
|
178
|
+
IQAUTH_CLIENT_ID: clientOut.clientId || "",
|
|
179
|
+
IQAUTH_PUBLISHABLE_KEY: pkOut,
|
|
180
|
+
IQAUTH_SECRET_KEY: skOut,
|
|
181
|
+
IQAUTH_REDIRECT_URI: args.redirect
|
|
182
|
+
};
|
|
183
|
+
if (clientOut.clientSecret) updates.IQAUTH_CLIENT_SECRET = clientOut.clientSecret;
|
|
184
|
+
for (const k of Object.keys(updates)) if (!updates[k]) delete updates[k];
|
|
185
|
+
await mergeEnv(args.envFile, updates);
|
|
186
|
+
console.log(`
|
|
187
|
+
\u2705 Wrote ${Object.keys(updates).length} IQAUTH_* variables to ${args.envFile}`);
|
|
188
|
+
console.log(` Add ${args.envFile} to .gitignore \u2014 it contains your secret key.`);
|
|
189
|
+
console.log(` Use IQAUTH_PUBLISHABLE_KEY in your front-end and IQAUTH_SECRET_KEY in your back-end.
|
|
190
|
+
`);
|
|
191
|
+
void ENV_KEYS;
|
|
192
|
+
}
|
|
193
|
+
var import_promises, import_node_readline, ENV_KEYS;
|
|
194
|
+
var init_init = __esm({
|
|
195
|
+
"src/cli/init.ts"() {
|
|
196
|
+
"use strict";
|
|
197
|
+
import_promises = require("fs/promises");
|
|
198
|
+
import_node_readline = require("readline");
|
|
199
|
+
ENV_KEYS = [
|
|
200
|
+
"IQAUTH_ISSUER",
|
|
201
|
+
"IQAUTH_APP_ID",
|
|
202
|
+
"IQAUTH_APP_KEY",
|
|
203
|
+
"IQAUTH_TENANT_ID",
|
|
204
|
+
"IQAUTH_CLIENT_ID",
|
|
205
|
+
"IQAUTH_CLIENT_SECRET",
|
|
206
|
+
"IQAUTH_PUBLISHABLE_KEY",
|
|
207
|
+
"IQAUTH_SECRET_KEY",
|
|
208
|
+
"IQAUTH_REDIRECT_URI"
|
|
209
|
+
];
|
|
210
|
+
main().catch((err) => {
|
|
211
|
+
console.error("\n\u2717", err.message);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// src/cli/util.ts
|
|
218
|
+
function parseFlags(argv) {
|
|
219
|
+
const flags = /* @__PURE__ */ new Map();
|
|
220
|
+
const bools = /* @__PURE__ */ new Set();
|
|
221
|
+
const positional = [];
|
|
222
|
+
for (let i = 0; i < argv.length; i++) {
|
|
223
|
+
const arg = argv[i];
|
|
224
|
+
if (arg.startsWith("--")) {
|
|
225
|
+
const key = arg.slice(2);
|
|
226
|
+
const next = argv[i + 1];
|
|
227
|
+
if (next && !next.startsWith("--")) {
|
|
228
|
+
flags.set(key, next);
|
|
229
|
+
i++;
|
|
230
|
+
} else {
|
|
231
|
+
bools.add(key);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
positional.push(arg);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { flags, bools, positional };
|
|
238
|
+
}
|
|
239
|
+
async function loadEnv(path = ".env") {
|
|
240
|
+
try {
|
|
241
|
+
await (0, import_promises2.access)(path);
|
|
242
|
+
} catch {
|
|
243
|
+
return { ...process.env };
|
|
244
|
+
}
|
|
245
|
+
const raw = await (0, import_promises2.readFile)(path, "utf8");
|
|
246
|
+
const out = { ...process.env };
|
|
247
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
248
|
+
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
249
|
+
if (!m) continue;
|
|
250
|
+
let val = m[2];
|
|
251
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
252
|
+
val = val.slice(1, -1);
|
|
253
|
+
}
|
|
254
|
+
if (out[m[1]] === void 0) out[m[1]] = val;
|
|
255
|
+
}
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
async function callApi(baseUrl, path, init = {}) {
|
|
259
|
+
const headers = { "Content-Type": "application/json", ...init.headers };
|
|
260
|
+
if (init.token) headers["Authorization"] = `Bearer ${init.token}`;
|
|
261
|
+
const res = await fetch(`${baseUrl.replace(/\/+$/, "")}${path}`, { ...init, headers });
|
|
262
|
+
const json = await res.json().catch(() => ({}));
|
|
263
|
+
if (!res.ok || json.success === false) {
|
|
264
|
+
const code = json.error?.code || res.status;
|
|
265
|
+
const msg = json.error?.message || res.statusText;
|
|
266
|
+
throw new Error(`${path} \u2192 ${code}: ${msg}`);
|
|
267
|
+
}
|
|
268
|
+
return json.data ?? json;
|
|
269
|
+
}
|
|
270
|
+
function symbol(ok) {
|
|
271
|
+
return ok ? "\u2713" : "\u2717";
|
|
272
|
+
}
|
|
273
|
+
var import_promises2;
|
|
274
|
+
var init_util = __esm({
|
|
275
|
+
"src/cli/util.ts"() {
|
|
276
|
+
"use strict";
|
|
277
|
+
import_promises2 = require("fs/promises");
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// src/publishableKey.ts
|
|
282
|
+
function b64urlDecode(input) {
|
|
283
|
+
const pad = input.length % 4 === 0 ? "" : "=".repeat(4 - input.length % 4);
|
|
284
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/") + pad;
|
|
285
|
+
if (typeof atob === "function") {
|
|
286
|
+
const bin = atob(normalized);
|
|
287
|
+
const bytes = new Uint8Array(bin.length);
|
|
288
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
289
|
+
return new TextDecoder().decode(bytes);
|
|
290
|
+
}
|
|
291
|
+
const { Buffer: Buffer2 } = require("buffer");
|
|
292
|
+
return Buffer2.from(normalized, "base64").toString("utf8");
|
|
293
|
+
}
|
|
294
|
+
function parsePublishableKey(raw) {
|
|
295
|
+
if (typeof raw !== "string") return null;
|
|
296
|
+
const m = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
297
|
+
if (!m) return null;
|
|
298
|
+
try {
|
|
299
|
+
const json = JSON.parse(b64urlDecode(m[2]));
|
|
300
|
+
if (!json || typeof json !== "object") return null;
|
|
301
|
+
if (typeof json.iss !== "string" || typeof json.appId !== "string" || typeof json.tenantId !== "string" || typeof json.kid !== "string") {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
|
|
305
|
+
} catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
var init_publishableKey = __esm({
|
|
310
|
+
"src/publishableKey.ts"() {
|
|
311
|
+
"use strict";
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// src/cli/doctor.ts
|
|
316
|
+
var doctor_exports = {};
|
|
317
|
+
__export(doctor_exports, {
|
|
318
|
+
runDoctor: () => runDoctor
|
|
319
|
+
});
|
|
320
|
+
async function runDoctor(argv) {
|
|
321
|
+
const { flags } = parseFlags(argv);
|
|
322
|
+
const envFile = flags.get("env-file") || ".env";
|
|
323
|
+
const env = await loadEnv(envFile);
|
|
324
|
+
const probes = [];
|
|
325
|
+
const pkRaw = env.IQAUTH_PUBLISHABLE_KEY;
|
|
326
|
+
const issuerEnv = env.IQAUTH_ISSUER;
|
|
327
|
+
const redirect = env.IQAUTH_REDIRECT_URI;
|
|
328
|
+
probes.push({
|
|
329
|
+
name: ".env present",
|
|
330
|
+
ok: !!pkRaw,
|
|
331
|
+
detail: pkRaw ? `${envFile} loaded; IQAUTH_PUBLISHABLE_KEY=${pkRaw.slice(0, 10)}\u2026` : `IQAUTH_PUBLISHABLE_KEY missing in ${envFile}`
|
|
332
|
+
});
|
|
333
|
+
const parsed = pkRaw ? parsePublishableKey(pkRaw) : null;
|
|
334
|
+
probes.push({
|
|
335
|
+
name: "publishable key parses",
|
|
336
|
+
ok: !!parsed,
|
|
337
|
+
detail: parsed ? `mode=${parsed.mode} appId=${parsed.appId} tenantId=${parsed.tenantId} kid=${parsed.kid}` : "key did not match pk_<test|live>_<base64> format"
|
|
338
|
+
});
|
|
339
|
+
const issuer = (issuerEnv || (parsed?.iss.startsWith("http") ? parsed.iss : parsed ? `https://${parsed.iss}` : "")).replace(/\/+$/, "");
|
|
340
|
+
if (issuer) {
|
|
341
|
+
try {
|
|
342
|
+
const res = await fetch(`${issuer}/.well-known/openid-configuration`);
|
|
343
|
+
probes.push({
|
|
344
|
+
name: "issuer reachable",
|
|
345
|
+
ok: res.ok,
|
|
346
|
+
detail: `${issuer}/.well-known/openid-configuration \u2192 ${res.status}`
|
|
347
|
+
});
|
|
348
|
+
} catch (err) {
|
|
349
|
+
probes.push({
|
|
350
|
+
name: "issuer reachable",
|
|
351
|
+
ok: false,
|
|
352
|
+
detail: `fetch failed: ${err.message}`
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
probes.push({ name: "issuer reachable", ok: false, detail: "issuer URL unknown (no IQAUTH_ISSUER and no key)" });
|
|
357
|
+
}
|
|
358
|
+
if (issuer) {
|
|
359
|
+
try {
|
|
360
|
+
const res = await fetch(`${issuer}/.well-known/jwks.json`);
|
|
361
|
+
const json = await res.json().catch(() => ({}));
|
|
362
|
+
const keys = json.keys;
|
|
363
|
+
probes.push({
|
|
364
|
+
name: "JWKS reachable",
|
|
365
|
+
ok: res.ok && Array.isArray(keys) && keys.length > 0,
|
|
366
|
+
detail: `${issuer}/.well-known/jwks.json \u2192 ${res.status} (${Array.isArray(keys) ? keys.length : 0} keys)`
|
|
367
|
+
});
|
|
368
|
+
} catch (err) {
|
|
369
|
+
probes.push({ name: "JWKS reachable", ok: false, detail: `fetch failed: ${err.message}` });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (redirect) {
|
|
373
|
+
try {
|
|
374
|
+
const res = await fetch(redirect, { method: "GET" });
|
|
375
|
+
probes.push({
|
|
376
|
+
name: "redirect URI reachable",
|
|
377
|
+
ok: res.status > 0 && res.status < 500,
|
|
378
|
+
detail: `${redirect} \u2192 ${res.status}`
|
|
379
|
+
});
|
|
380
|
+
} catch (err) {
|
|
381
|
+
probes.push({ name: "redirect URI reachable", ok: false, detail: `fetch failed: ${err.message}` });
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
probes.push({ name: "redirect URI reachable", ok: false, detail: "IQAUTH_REDIRECT_URI not set" });
|
|
385
|
+
}
|
|
386
|
+
let allOk = true;
|
|
387
|
+
for (const p of probes) {
|
|
388
|
+
console.log(`${symbol(p.ok)} ${p.name.padEnd(28)} ${p.detail}`);
|
|
389
|
+
if (!p.ok) allOk = false;
|
|
390
|
+
}
|
|
391
|
+
console.log("");
|
|
392
|
+
console.log(allOk ? "\u2705 All checks passed." : "\u274C One or more checks failed \u2014 see above.");
|
|
393
|
+
process.exit(allOk ? 0 : 1);
|
|
394
|
+
}
|
|
395
|
+
var init_doctor = __esm({
|
|
396
|
+
"src/cli/doctor.ts"() {
|
|
397
|
+
"use strict";
|
|
398
|
+
init_util();
|
|
399
|
+
init_publishableKey();
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// src/cli/keys.ts
|
|
404
|
+
var keys_exports = {};
|
|
405
|
+
__export(keys_exports, {
|
|
406
|
+
runKeys: () => runKeys
|
|
407
|
+
});
|
|
408
|
+
async function getCtx(flags) {
|
|
409
|
+
const env = await loadEnv(flags.get("env-file") || ".env");
|
|
410
|
+
const baseUrl = flags.get("base-url") || env.IQAUTH_ISSUER;
|
|
411
|
+
const token = flags.get("token") || env.IQAUTH_ADMIN_TOKEN || env.IQAUTH_SECRET_KEY;
|
|
412
|
+
const app = flags.get("app") || env.IQAUTH_APP_ID || env.IQAUTH_APP_KEY;
|
|
413
|
+
if (!baseUrl) throw new Error("Missing --base-url (or IQAUTH_ISSUER in env).");
|
|
414
|
+
if (!token) throw new Error("Missing --token (or IQAUTH_ADMIN_TOKEN / IQAUTH_SECRET_KEY in env).");
|
|
415
|
+
if (!app) throw new Error("Missing --app <appId|appKey> (or IQAUTH_APP_ID in env).");
|
|
416
|
+
return { baseUrl, token, app };
|
|
417
|
+
}
|
|
418
|
+
async function runKeys(argv) {
|
|
419
|
+
const action = argv[0];
|
|
420
|
+
const { flags, bools } = parseFlags(argv.slice(1));
|
|
421
|
+
if (action === "list") {
|
|
422
|
+
const ctx = await getCtx(flags);
|
|
423
|
+
const keys = await callApi(ctx.baseUrl, `/api/v1/apps/${ctx.app}/keys`, { method: "GET", token: ctx.token });
|
|
424
|
+
const header = ["KIND", "MODE", "ID", "PREFIX", "CREATED", "REVOKED"].join(" ");
|
|
425
|
+
console.log(header);
|
|
426
|
+
for (const k of keys) {
|
|
427
|
+
console.log([k.kind, k.mode, k.id, k.prefix || "", k.createdAt || "", k.revokedAt || "-"].join(" "));
|
|
428
|
+
}
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (action === "rotate") {
|
|
432
|
+
const ctx = await getCtx(flags);
|
|
433
|
+
const keyId = flags.get("key-id");
|
|
434
|
+
if (!keyId) throw new Error("Missing --key-id <id>");
|
|
435
|
+
if (!bools.has("yes")) {
|
|
436
|
+
console.log(`Rotate key ${keyId} for app ${ctx.app}? Previous value valid 24h. Re-run with --yes to proceed.`);
|
|
437
|
+
process.exit(2);
|
|
438
|
+
}
|
|
439
|
+
const out = await callApi(ctx.baseUrl, `/api/v1/apps/${ctx.app}/keys/${keyId}/rotate`, { method: "POST", token: ctx.token });
|
|
440
|
+
console.log(`\u2713 rotated. New value:
|
|
441
|
+
${out.rawKey}`);
|
|
442
|
+
console.log(" Old value remains valid for 24h.");
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (action === "revoke") {
|
|
446
|
+
const ctx = await getCtx(flags);
|
|
447
|
+
const keyId = flags.get("key-id");
|
|
448
|
+
if (!keyId) throw new Error("Missing --key-id <id>");
|
|
449
|
+
if (!bools.has("yes")) {
|
|
450
|
+
console.log(`Revoke key ${keyId} for app ${ctx.app}? IMMEDIATE \u2014 there is no grace window. Re-run with --yes.`);
|
|
451
|
+
process.exit(2);
|
|
452
|
+
}
|
|
453
|
+
await callApi(ctx.baseUrl, `/api/v1/apps/${ctx.app}/keys/${keyId}`, { method: "DELETE", token: ctx.token });
|
|
454
|
+
console.log("\u2713 revoked.");
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
console.error("Usage: iqauth keys <list|rotate|revoke> [--app <id>] [--key-id <id>] [--yes]");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
var init_keys = __esm({
|
|
461
|
+
"src/cli/keys.ts"() {
|
|
462
|
+
"use strict";
|
|
463
|
+
init_util();
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// src/cli/dev.ts
|
|
468
|
+
var dev_exports = {};
|
|
469
|
+
__export(dev_exports, {
|
|
470
|
+
runDev: () => runDev
|
|
471
|
+
});
|
|
472
|
+
function getHere() {
|
|
473
|
+
const cjsDir = globalThis.__dirname;
|
|
474
|
+
if (typeof cjsDir === "string" && cjsDir.length > 0) return cjsDir;
|
|
475
|
+
try {
|
|
476
|
+
const url = import_meta?.url;
|
|
477
|
+
if (typeof url === "string" && url.length > 0) return (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(url));
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
const argv1 = process.argv[1];
|
|
481
|
+
if (argv1) return (0, import_node_path.dirname)(argv1);
|
|
482
|
+
return process.cwd();
|
|
483
|
+
}
|
|
484
|
+
async function runDev(argv) {
|
|
485
|
+
const { flags } = parseFlags(argv);
|
|
486
|
+
const env = await loadEnv(flags.get("env-file") || ".env");
|
|
487
|
+
const pk = env.IQAUTH_PUBLISHABLE_KEY;
|
|
488
|
+
if (!pk) {
|
|
489
|
+
console.error("\u2717 IQAUTH_PUBLISHABLE_KEY missing in .env. Run `iqauth init` first.");
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
const here = getHere();
|
|
493
|
+
let example = flags.get("example");
|
|
494
|
+
if (!example) {
|
|
495
|
+
const candidates = [
|
|
496
|
+
(0, import_node_path.resolve)(here, "..", "..", "..", "..", "examples", "react-vite"),
|
|
497
|
+
(0, import_node_path.resolve)(here, "..", "..", "examples", "react-vite"),
|
|
498
|
+
(0, import_node_path.resolve)(process.cwd(), "examples", "react-vite")
|
|
499
|
+
];
|
|
500
|
+
example = candidates.find((p) => (0, import_node_fs.existsSync)(p)) || candidates[0];
|
|
501
|
+
}
|
|
502
|
+
console.log(`\u2192 Starting example at ${example}`);
|
|
503
|
+
console.log(` using IQAUTH_PUBLISHABLE_KEY=${pk.slice(0, 12)}\u2026`);
|
|
504
|
+
const child = (0, import_node_child_process.spawn)("npm", ["run", "dev"], {
|
|
505
|
+
cwd: example,
|
|
506
|
+
stdio: "inherit",
|
|
507
|
+
env: {
|
|
508
|
+
...process.env,
|
|
509
|
+
VITE_IQAUTH_PUBLISHABLE_KEY: pk
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
513
|
+
}
|
|
514
|
+
var import_node_child_process, import_node_path, import_node_url, import_node_fs, import_meta;
|
|
515
|
+
var init_dev = __esm({
|
|
516
|
+
"src/cli/dev.ts"() {
|
|
517
|
+
"use strict";
|
|
518
|
+
import_node_child_process = require("child_process");
|
|
519
|
+
import_node_path = require("path");
|
|
520
|
+
import_node_url = require("url");
|
|
521
|
+
import_node_fs = require("fs");
|
|
522
|
+
init_util();
|
|
523
|
+
import_meta = {};
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// src/cli/index.ts
|
|
528
|
+
var subcommand = process.argv[2];
|
|
529
|
+
var rest = process.argv.slice(3);
|
|
530
|
+
async function run() {
|
|
531
|
+
switch (subcommand) {
|
|
532
|
+
case void 0:
|
|
533
|
+
case "--help":
|
|
534
|
+
case "-h":
|
|
535
|
+
case "help":
|
|
536
|
+
printHelp();
|
|
537
|
+
return;
|
|
538
|
+
case "init": {
|
|
539
|
+
process.argv = [process.argv[0], process.argv[1], ...rest];
|
|
540
|
+
await Promise.resolve().then(() => (init_init(), init_exports));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
case "doctor": {
|
|
544
|
+
const { runDoctor: runDoctor2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
|
|
545
|
+
await runDoctor2(rest);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
case "keys": {
|
|
549
|
+
const { runKeys: runKeys2 } = await Promise.resolve().then(() => (init_keys(), keys_exports));
|
|
550
|
+
await runKeys2(rest);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
case "dev": {
|
|
554
|
+
const { runDev: runDev2 } = await Promise.resolve().then(() => (init_dev(), dev_exports));
|
|
555
|
+
await runDev2(rest);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
default:
|
|
559
|
+
console.error(`Unknown command: ${subcommand}`);
|
|
560
|
+
printHelp();
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function printHelp() {
|
|
565
|
+
console.log(`iqauth \u2014 IQAuth integration CLI
|
|
566
|
+
|
|
567
|
+
Usage:
|
|
568
|
+
iqauth <command> [options]
|
|
569
|
+
|
|
570
|
+
Commands:
|
|
571
|
+
init Bootstrap a new app and write IQAUTH_* keys to .env
|
|
572
|
+
doctor Check your .env, issuer reachability, and publishable key
|
|
573
|
+
keys Manage app publishable / secret keys (list / rotate / revoke)
|
|
574
|
+
dev Run the bundled React example app against your IQAUTH_PUBLISHABLE_KEY
|
|
575
|
+
|
|
576
|
+
Run \`iqauth <command> --help\` for command-specific help.`);
|
|
577
|
+
}
|
|
578
|
+
run().catch((err) => {
|
|
579
|
+
console.error("\n\u2717", err?.message || err);
|
|
580
|
+
process.exit(1);
|
|
581
|
+
});
|