seamless-cli 0.5.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -0
- package/dist/commands/adminShared.js +23 -0
- package/dist/commands/adminShared.js.map +1 -0
- package/dist/commands/config.js +187 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/help.js +98 -0
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.js +182 -26
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.js +86 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.js +43 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/org.js +249 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/profile.js +150 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/sessions.js +112 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/users.js +147 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/verify.js +133 -9
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/whoami.js +29 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/core/admin.js +142 -0
- package/dist/core/admin.js.map +1 -0
- package/dist/core/args.js +19 -0
- package/dist/core/args.js.map +1 -0
- package/dist/core/authClient.js +113 -0
- package/dist/core/authClient.js.map +1 -0
- package/dist/core/config.js +146 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/http.js +34 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/images.js +1 -1
- package/dist/core/keychain.js +112 -0
- package/dist/core/keychain.js.map +1 -0
- package/dist/core/loginFlow.js +143 -0
- package/dist/core/loginFlow.js.map +1 -0
- package/dist/core/output.js +46 -0
- package/dist/core/output.js.map +1 -1
- package/dist/core/portal.js +74 -0
- package/dist/core/portal.js.map +1 -0
- package/dist/core/session.js +30 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/sessions.js +41 -0
- package/dist/core/sessions.js.map +1 -0
- package/dist/core/systemConfig.js +125 -0
- package/dist/core/systemConfig.js.map +1 -0
- package/dist/generators/config/config.js +30 -15
- package/dist/generators/config/config.js.map +1 -1
- package/dist/index.js +51 -3
- package/dist/index.js.map +1 -1
- package/dist/prompts/appSelect.js +41 -0
- package/dist/prompts/appSelect.js.map +1 -0
- package/dist/prompts/projectSetup.js +26 -0
- package/dist/prompts/projectSetup.js.map +1 -1
- package/package.json +13 -2
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { getActiveProfile } from "./config.js";
|
|
2
|
+
import { deleteTokens, getTokens, KeychainUnavailableError, saveTokens, } from "./keychain.js";
|
|
3
|
+
import { apiRequest, joinUrl } from "./http.js";
|
|
4
|
+
export class ReauthRequiredError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ReauthRequiredError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function tokensFromAuthResponse(data) {
|
|
11
|
+
if (!data)
|
|
12
|
+
return null;
|
|
13
|
+
const accessToken = typeof data.token === "string" ? data.token : undefined;
|
|
14
|
+
const refreshToken = typeof data.refreshToken === "string" ? data.refreshToken : undefined;
|
|
15
|
+
if (!accessToken || !refreshToken)
|
|
16
|
+
return null;
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const ttl = typeof data.ttl === "number" ? data.ttl : undefined;
|
|
19
|
+
const refreshTtl = typeof data.refreshTtl === "number" ? data.refreshTtl : undefined;
|
|
20
|
+
return {
|
|
21
|
+
accessToken,
|
|
22
|
+
refreshToken,
|
|
23
|
+
accessTokenExpiresAt: ttl !== undefined ? now + ttl * 1000 : undefined,
|
|
24
|
+
refreshTokenExpiresAt: refreshTtl !== undefined ? now + refreshTtl * 1000 : undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export async function createAuthClient(opts = {}) {
|
|
28
|
+
const profile = getActiveProfile(opts);
|
|
29
|
+
if (!profile) {
|
|
30
|
+
throw new ReauthRequiredError("No active profile is configured. Add one with: seamless profile add <name> --instance-url <url>, then run seamless login.");
|
|
31
|
+
}
|
|
32
|
+
let tokens = await getTokens(profile);
|
|
33
|
+
if (!tokens || !tokens.refreshToken) {
|
|
34
|
+
throw new ReauthRequiredError(`No session for profile "${profile.name}". Run: seamless login.`);
|
|
35
|
+
}
|
|
36
|
+
const session = tokens;
|
|
37
|
+
const persist = async (next) => {
|
|
38
|
+
session.accessToken = next.accessToken;
|
|
39
|
+
session.refreshToken = next.refreshToken;
|
|
40
|
+
session.accessTokenExpiresAt = next.accessTokenExpiresAt;
|
|
41
|
+
session.refreshTokenExpiresAt = next.refreshTokenExpiresAt;
|
|
42
|
+
try {
|
|
43
|
+
await saveTokens(profile, next);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (!(err instanceof KeychainUnavailableError))
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const clearSession = async () => {
|
|
51
|
+
try {
|
|
52
|
+
await deleteTokens(profile);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (!(err instanceof KeychainUnavailableError))
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const refresh = async () => {
|
|
60
|
+
const res = await apiRequest(joinUrl(profile.instanceUrl, "/refresh"), {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { Authorization: `Bearer ${session.refreshToken}` },
|
|
63
|
+
});
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
await clearSession();
|
|
66
|
+
throw new ReauthRequiredError(`Your session for "${profile.name}" has expired or was revoked. Run: seamless login.`);
|
|
67
|
+
}
|
|
68
|
+
const next = tokensFromAuthResponse(res.data);
|
|
69
|
+
if (!next) {
|
|
70
|
+
await clearSession();
|
|
71
|
+
throw new ReauthRequiredError(`Received an unexpected refresh response from ${profile.instanceUrl}. Run: seamless login.`);
|
|
72
|
+
}
|
|
73
|
+
await persist(next);
|
|
74
|
+
};
|
|
75
|
+
const authedFetch = async (path, init = {}) => {
|
|
76
|
+
const build = () => ({
|
|
77
|
+
...init,
|
|
78
|
+
headers: {
|
|
79
|
+
...init.headers,
|
|
80
|
+
Authorization: `Bearer ${session.accessToken}`,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const url = joinUrl(profile.instanceUrl, path);
|
|
84
|
+
let res = await apiRequest(url, build());
|
|
85
|
+
if (res.status === 401 && session.refreshToken) {
|
|
86
|
+
await refresh();
|
|
87
|
+
res = await apiRequest(url, build());
|
|
88
|
+
}
|
|
89
|
+
return res;
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
profile,
|
|
93
|
+
request: authedFetch,
|
|
94
|
+
get: (path, init = {}) => authedFetch(path, { ...init, method: "GET" }),
|
|
95
|
+
post: (path, body, init = {}) => {
|
|
96
|
+
const headers = {
|
|
97
|
+
...init.headers,
|
|
98
|
+
};
|
|
99
|
+
let bodyInit = init.body;
|
|
100
|
+
if (body !== undefined) {
|
|
101
|
+
headers["Content-Type"] = "application/json";
|
|
102
|
+
bodyInit = JSON.stringify(body);
|
|
103
|
+
}
|
|
104
|
+
return authedFetch(path, {
|
|
105
|
+
...init,
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers,
|
|
108
|
+
body: bodyInit,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=authClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authClient.js","sourceRoot":"","sources":["../../src/core/authClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
export const DEFAULT_PROFILE_NAME = "default";
|
|
5
|
+
export function getConfigDir() {
|
|
6
|
+
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
7
|
+
const base = xdg ? xdg : path.join(os.homedir(), ".config");
|
|
8
|
+
return path.join(base, "seamless");
|
|
9
|
+
}
|
|
10
|
+
export function getConfigPath() {
|
|
11
|
+
return path.join(getConfigDir(), "config.json");
|
|
12
|
+
}
|
|
13
|
+
function emptyConfig() {
|
|
14
|
+
return { activeProfile: DEFAULT_PROFILE_NAME, profiles: {} };
|
|
15
|
+
}
|
|
16
|
+
function coerceProfile(name, value) {
|
|
17
|
+
if (!value || typeof value !== "object")
|
|
18
|
+
return undefined;
|
|
19
|
+
const raw = value;
|
|
20
|
+
if (typeof raw.instanceUrl !== "string")
|
|
21
|
+
return undefined;
|
|
22
|
+
const profile = { name, instanceUrl: raw.instanceUrl };
|
|
23
|
+
if (typeof raw.sub === "string")
|
|
24
|
+
profile.sub = raw.sub;
|
|
25
|
+
if (typeof raw.email === "string")
|
|
26
|
+
profile.email = raw.email;
|
|
27
|
+
if (raw.identifierType === "email" || raw.identifierType === "phone") {
|
|
28
|
+
profile.identifierType = raw.identifierType;
|
|
29
|
+
}
|
|
30
|
+
return profile;
|
|
31
|
+
}
|
|
32
|
+
function normalizeLoaded(parsed) {
|
|
33
|
+
if (!parsed || typeof parsed !== "object")
|
|
34
|
+
return emptyConfig();
|
|
35
|
+
const raw = parsed;
|
|
36
|
+
const profiles = {};
|
|
37
|
+
if (raw.profiles && typeof raw.profiles === "object") {
|
|
38
|
+
for (const [name, value] of Object.entries(raw.profiles)) {
|
|
39
|
+
const profile = coerceProfile(name, value);
|
|
40
|
+
if (profile)
|
|
41
|
+
profiles[name] = profile;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const active = typeof raw.activeProfile === "string" && raw.activeProfile
|
|
45
|
+
? raw.activeProfile
|
|
46
|
+
: DEFAULT_PROFILE_NAME;
|
|
47
|
+
return { activeProfile: active, profiles };
|
|
48
|
+
}
|
|
49
|
+
export function loadConfig() {
|
|
50
|
+
const file = getConfigPath();
|
|
51
|
+
if (!fs.existsSync(file))
|
|
52
|
+
return emptyConfig();
|
|
53
|
+
let raw;
|
|
54
|
+
try {
|
|
55
|
+
raw = fs.readFileSync(file, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new Error(`Unable to read config at ${file}: ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return normalizeLoaded(JSON.parse(raw));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error(`Config at ${file} is not valid JSON. Fix or remove it and try again.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function saveConfig(config) {
|
|
68
|
+
const dir = getConfigDir();
|
|
69
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
70
|
+
const file = getConfigPath();
|
|
71
|
+
const tmp = `${file}.tmp`;
|
|
72
|
+
fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", { mode: 0o600 });
|
|
73
|
+
fs.renameSync(tmp, file);
|
|
74
|
+
}
|
|
75
|
+
export function listProfiles(config = loadConfig()) {
|
|
76
|
+
return Object.values(config.profiles);
|
|
77
|
+
}
|
|
78
|
+
export function getProfile(name, config = loadConfig()) {
|
|
79
|
+
return config.profiles[name];
|
|
80
|
+
}
|
|
81
|
+
export function upsertProfile(profile) {
|
|
82
|
+
const config = loadConfig();
|
|
83
|
+
config.profiles[profile.name] = profile;
|
|
84
|
+
if (!config.profiles[config.activeProfile]) {
|
|
85
|
+
config.activeProfile = profile.name;
|
|
86
|
+
}
|
|
87
|
+
saveConfig(config);
|
|
88
|
+
return config;
|
|
89
|
+
}
|
|
90
|
+
export function removeProfile(name) {
|
|
91
|
+
const config = loadConfig();
|
|
92
|
+
if (!config.profiles[name]) {
|
|
93
|
+
throw new Error(`Profile "${name}" does not exist.`);
|
|
94
|
+
}
|
|
95
|
+
delete config.profiles[name];
|
|
96
|
+
if (config.activeProfile === name) {
|
|
97
|
+
config.activeProfile = Object.keys(config.profiles)[0] ?? DEFAULT_PROFILE_NAME;
|
|
98
|
+
}
|
|
99
|
+
saveConfig(config);
|
|
100
|
+
return config;
|
|
101
|
+
}
|
|
102
|
+
export function setActiveProfile(name) {
|
|
103
|
+
const config = loadConfig();
|
|
104
|
+
if (!config.profiles[name]) {
|
|
105
|
+
throw new Error(`Profile "${name}" does not exist. Add it with "seamless profile add ${name}".`);
|
|
106
|
+
}
|
|
107
|
+
config.activeProfile = name;
|
|
108
|
+
saveConfig(config);
|
|
109
|
+
return config;
|
|
110
|
+
}
|
|
111
|
+
export function resolveActiveProfileName(opts = {}, config = loadConfig()) {
|
|
112
|
+
return (opts.profileFlag?.trim() ||
|
|
113
|
+
process.env.SEAMLESS_PROFILE?.trim() ||
|
|
114
|
+
config.activeProfile ||
|
|
115
|
+
DEFAULT_PROFILE_NAME);
|
|
116
|
+
}
|
|
117
|
+
export function getActiveProfile(opts = {}) {
|
|
118
|
+
const config = loadConfig();
|
|
119
|
+
return config.profiles[resolveActiveProfileName(opts, config)];
|
|
120
|
+
}
|
|
121
|
+
const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
122
|
+
export function normalizeInstanceUrl(input) {
|
|
123
|
+
const trimmed = (input ?? "").trim();
|
|
124
|
+
if (!trimmed) {
|
|
125
|
+
throw new Error("Instance URL is required.");
|
|
126
|
+
}
|
|
127
|
+
let url;
|
|
128
|
+
try {
|
|
129
|
+
url = new URL(trimmed);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
throw new Error(`Invalid instance URL: "${input}". Include the scheme, for example https://auth.example.com.`);
|
|
133
|
+
}
|
|
134
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
135
|
+
throw new Error(`Instance URL must use http or https, got "${url.protocol}".`);
|
|
136
|
+
}
|
|
137
|
+
const host = url.hostname;
|
|
138
|
+
const isLocal = LOCAL_HOSTS.has(host) || host.endsWith(".localhost");
|
|
139
|
+
if (url.protocol === "http:" && !isLocal) {
|
|
140
|
+
throw new Error(`Instance URL must use https for non-local host "${host}".`);
|
|
141
|
+
}
|
|
142
|
+
url.hash = "";
|
|
143
|
+
url.search = "";
|
|
144
|
+
return url.toString().replace(/\/+$/, "");
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function joinUrl(base, path) {
|
|
2
|
+
if (/^https?:\/\//i.test(path))
|
|
3
|
+
return path;
|
|
4
|
+
return `${base}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
5
|
+
}
|
|
6
|
+
export async function safeJson(res) {
|
|
7
|
+
const text = await res.text();
|
|
8
|
+
if (!text)
|
|
9
|
+
return null;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(text);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function apiRequest(url, init = {}) {
|
|
18
|
+
const res = await fetch(url, init);
|
|
19
|
+
const data = await safeJson(res);
|
|
20
|
+
return { ok: res.ok, status: res.status, data, headers: res.headers };
|
|
21
|
+
}
|
|
22
|
+
export function isRateLimited(res) {
|
|
23
|
+
return res.status === 429;
|
|
24
|
+
}
|
|
25
|
+
export function jsonBody(method, body, headers = {}) {
|
|
26
|
+
const init = { method, headers: { ...headers } };
|
|
27
|
+
if (body !== undefined) {
|
|
28
|
+
init.headers["Content-Type"] =
|
|
29
|
+
"application/json";
|
|
30
|
+
init.body = JSON.stringify(body);
|
|
31
|
+
}
|
|
32
|
+
return init;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAY;IAChD,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAc,GAAa;IACvD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,OAAoB,EAAE;IAEtB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAI,GAAG,CAAC,CAAC;IACpC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAgC;IAC5D,OAAO,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,MAAc,EACd,IAAc,EACd,UAAkC,EAAE;IAEpC,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC;IAC9D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,CAAC,OAAkC,CAAC,cAAc,CAAC;YACtD,kBAAkB,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/core/images.js
CHANGED
|
@@ -7,5 +7,5 @@ export const SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE = `ghcr.io/fells-code/seamless-
|
|
|
7
7
|
// CLI version always produces the same project. Override the ref with
|
|
8
8
|
// SEAMLESS_TEMPLATES_REF, or point at a local checkout with SEAMLESS_TEMPLATES_DIR.
|
|
9
9
|
export const SEAMLESS_TEMPLATES_REPO = "fells-code/seamless-templates";
|
|
10
|
-
export const SEAMLESS_TEMPLATES_REF = "v0.2.
|
|
10
|
+
export const SEAMLESS_TEMPLATES_REF = "v0.2.4";
|
|
11
11
|
//# sourceMappingURL=images.js.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const SERVICE = "seamless-cli";
|
|
2
|
+
export class KeychainUnavailableError extends Error {
|
|
3
|
+
constructor(cause) {
|
|
4
|
+
super("No OS keychain is available. Set SEAMLESS_REFRESH_TOKEN to authenticate in a headless environment, or run on a machine with a keychain (macOS Keychain, Windows Credential Manager, or Linux Secret Service).");
|
|
5
|
+
this.name = "KeychainUnavailableError";
|
|
6
|
+
if (cause !== undefined)
|
|
7
|
+
this.cause = cause;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function accountKey(profile) {
|
|
11
|
+
return `${profile.name}::${profile.instanceUrl}`;
|
|
12
|
+
}
|
|
13
|
+
function isNoEntry(err) {
|
|
14
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15
|
+
return /no (matching )?entry/i.test(message) || /not found/i.test(message);
|
|
16
|
+
}
|
|
17
|
+
let backend = null;
|
|
18
|
+
async function loadBackend() {
|
|
19
|
+
if (backend)
|
|
20
|
+
return backend;
|
|
21
|
+
let Entry;
|
|
22
|
+
try {
|
|
23
|
+
({ Entry } = await import("@napi-rs/keyring"));
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
throw new KeychainUnavailableError(err);
|
|
27
|
+
}
|
|
28
|
+
backend = {
|
|
29
|
+
get(account) {
|
|
30
|
+
try {
|
|
31
|
+
return new Entry(SERVICE, account).getPassword();
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (isNoEntry(err))
|
|
35
|
+
return null;
|
|
36
|
+
throw new KeychainUnavailableError(err);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
set(account, secret) {
|
|
40
|
+
try {
|
|
41
|
+
new Entry(SERVICE, account).setPassword(secret);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw new KeychainUnavailableError(err);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
delete(account) {
|
|
48
|
+
try {
|
|
49
|
+
return new Entry(SERVICE, account).deletePassword();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (isNoEntry(err))
|
|
53
|
+
return false;
|
|
54
|
+
throw new KeychainUnavailableError(err);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
return backend;
|
|
59
|
+
}
|
|
60
|
+
export function setBackendForTesting(fake) {
|
|
61
|
+
backend = fake;
|
|
62
|
+
}
|
|
63
|
+
export async function saveTokens(profile, bundle) {
|
|
64
|
+
const b = await loadBackend();
|
|
65
|
+
b.set(accountKey(profile), JSON.stringify(bundle));
|
|
66
|
+
}
|
|
67
|
+
export async function getTokens(profile) {
|
|
68
|
+
const envRefresh = process.env.SEAMLESS_REFRESH_TOKEN?.trim();
|
|
69
|
+
if (envRefresh) {
|
|
70
|
+
return { accessToken: "", refreshToken: envRefresh };
|
|
71
|
+
}
|
|
72
|
+
const b = await loadBackend();
|
|
73
|
+
const raw = b.get(accountKey(profile));
|
|
74
|
+
if (!raw)
|
|
75
|
+
return null;
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(raw);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export async function deleteTokens(profile) {
|
|
84
|
+
const b = await loadBackend();
|
|
85
|
+
return b.delete(accountKey(profile));
|
|
86
|
+
}
|
|
87
|
+
export function redactToken(value) {
|
|
88
|
+
return value ? "[redacted]" : "(none)";
|
|
89
|
+
}
|
|
90
|
+
const TOKEN_KEYS = new Set([
|
|
91
|
+
"token",
|
|
92
|
+
"accesstoken",
|
|
93
|
+
"refreshtoken",
|
|
94
|
+
"verificationtoken",
|
|
95
|
+
"authorization",
|
|
96
|
+
]);
|
|
97
|
+
export function scrubTokens(value) {
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
return value.map((item) => scrubTokens(item));
|
|
100
|
+
}
|
|
101
|
+
if (value && typeof value === "object") {
|
|
102
|
+
const out = {};
|
|
103
|
+
for (const [key, val] of Object.entries(value)) {
|
|
104
|
+
out[key] = TOKEN_KEYS.has(key.toLowerCase())
|
|
105
|
+
? redactToken(typeof val === "string" ? val : String(val))
|
|
106
|
+
: scrubTokens(val);
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/core/keychain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { apiRequest, isRateLimited, joinUrl, jsonBody } from "./http.js";
|
|
2
|
+
import { tokensFromAuthResponse } from "./authClient.js";
|
|
3
|
+
export const EPHEMERAL_WINDOW_MS = 5 * 60 * 1000;
|
|
4
|
+
export const DEFAULT_MAX_ATTEMPTS = 3;
|
|
5
|
+
export class LoginError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "LoginError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function apiMessage(data) {
|
|
12
|
+
if (data && typeof data === "object") {
|
|
13
|
+
const record = data;
|
|
14
|
+
if (typeof record.error === "string")
|
|
15
|
+
return record.error;
|
|
16
|
+
if (typeof record.message === "string")
|
|
17
|
+
return record.message;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
async function request(instanceUrl, url, init) {
|
|
22
|
+
try {
|
|
23
|
+
return await apiRequest(url, init);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new LoginError(`Could not reach ${instanceUrl}. Check the instance URL and your connection.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function startLogin(instanceUrl, identifier, now) {
|
|
30
|
+
const res = await request(instanceUrl, joinUrl(instanceUrl, "/login"), jsonBody("POST", { identifier }));
|
|
31
|
+
if (isRateLimited(res)) {
|
|
32
|
+
throw new LoginError("The instance is rate limiting requests. Wait a few minutes and try again.");
|
|
33
|
+
}
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const message = apiMessage(res.data) ?? "";
|
|
36
|
+
if (res.status === 401 && /verify/i.test(message)) {
|
|
37
|
+
throw new LoginError(`The account for ${identifier} is not verified yet. Finish registration, then log in.`);
|
|
38
|
+
}
|
|
39
|
+
if (res.status === 400) {
|
|
40
|
+
throw new LoginError(`"${identifier}" is not a valid email or phone number.`);
|
|
41
|
+
}
|
|
42
|
+
if (res.status === 401 || res.status === 403) {
|
|
43
|
+
throw new LoginError(`No account was found for ${identifier}, or login is not permitted.`);
|
|
44
|
+
}
|
|
45
|
+
throw new LoginError(`Login request failed (${res.status}).`);
|
|
46
|
+
}
|
|
47
|
+
const data = res.data ?? {};
|
|
48
|
+
const ephemeralToken = typeof data.token === "string" ? data.token : "";
|
|
49
|
+
if (!ephemeralToken) {
|
|
50
|
+
throw new LoginError("The instance did not return a login token.");
|
|
51
|
+
}
|
|
52
|
+
const loginMethods = Array.isArray(data.loginMethods)
|
|
53
|
+
? data.loginMethods.filter((m) => typeof m === "string")
|
|
54
|
+
: [];
|
|
55
|
+
const channel = data.identifierType === "phone" ? "phone" : "email";
|
|
56
|
+
const sub = typeof data.sub === "string" ? data.sub : undefined;
|
|
57
|
+
return {
|
|
58
|
+
ephemeralToken,
|
|
59
|
+
loginMethods,
|
|
60
|
+
channel,
|
|
61
|
+
sub,
|
|
62
|
+
deadline: now() + EPHEMERAL_WINDOW_MS,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function sendCode(instanceUrl, started) {
|
|
66
|
+
const path = started.channel === "email"
|
|
67
|
+
? "/otp/generate-login-email-otp"
|
|
68
|
+
: "/otp/generate-login-phone-otp";
|
|
69
|
+
const res = await request(instanceUrl, joinUrl(instanceUrl, path), {
|
|
70
|
+
method: "GET",
|
|
71
|
+
headers: { Authorization: `Bearer ${started.ephemeralToken}` },
|
|
72
|
+
});
|
|
73
|
+
if (isRateLimited(res)) {
|
|
74
|
+
throw new LoginError("Too many code requests. The instance limits OTP to 10 per 15 minutes per IP. Wait and try again.");
|
|
75
|
+
}
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
if (res.status === 403) {
|
|
78
|
+
const label = started.channel === "email" ? "Email" : "Phone";
|
|
79
|
+
throw new LoginError(`${label} OTP login is disabled on this instance.`);
|
|
80
|
+
}
|
|
81
|
+
const message = apiMessage(res.data);
|
|
82
|
+
throw new LoginError(message ? `Could not send a code: ${message}` : "Could not send a login code.");
|
|
83
|
+
}
|
|
84
|
+
const refreshed = typeof res.data?.token === "string" ? res.data.token : undefined;
|
|
85
|
+
if (refreshed)
|
|
86
|
+
started.ephemeralToken = refreshed;
|
|
87
|
+
}
|
|
88
|
+
async function verifyCode(instanceUrl, started, code) {
|
|
89
|
+
const path = started.channel === "email"
|
|
90
|
+
? "/otp/verify-login-email-otp"
|
|
91
|
+
: "/otp/verify-login-phone-otp";
|
|
92
|
+
return request(instanceUrl, joinUrl(instanceUrl, path), jsonBody("POST", { verificationToken: code }, { Authorization: `Bearer ${started.ephemeralToken}` }));
|
|
93
|
+
}
|
|
94
|
+
export async function completeLogin(opts) {
|
|
95
|
+
const now = opts.now ?? (() => Date.now());
|
|
96
|
+
const maxAttempts = opts.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
97
|
+
const notify = opts.notify ?? (() => { });
|
|
98
|
+
let started = await startLogin(opts.instanceUrl, opts.identifier, now);
|
|
99
|
+
const channel = started.channel;
|
|
100
|
+
const required = channel === "email" ? "email_otp" : "phone_otp";
|
|
101
|
+
if (started.loginMethods.length > 0 && !started.loginMethods.includes(required)) {
|
|
102
|
+
throw new LoginError(`This account cannot use ${required.replace("_", " ")} login. Available methods: ${started.loginMethods.join(", ")}.`);
|
|
103
|
+
}
|
|
104
|
+
await sendCode(opts.instanceUrl, started);
|
|
105
|
+
notify({ type: "code_sent", channel });
|
|
106
|
+
let attempt = 0;
|
|
107
|
+
let resent = false;
|
|
108
|
+
while (attempt < maxAttempts) {
|
|
109
|
+
const code = await opts.getCode({ attempt: attempt + 1, resent });
|
|
110
|
+
resent = false;
|
|
111
|
+
if (code === null)
|
|
112
|
+
return null;
|
|
113
|
+
if (now() >= started.deadline) {
|
|
114
|
+
started = await startLogin(opts.instanceUrl, opts.identifier, now);
|
|
115
|
+
await sendCode(opts.instanceUrl, started);
|
|
116
|
+
resent = true;
|
|
117
|
+
notify({ type: "code_resent", channel });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
notify({ type: "verifying" });
|
|
121
|
+
const res = await verifyCode(opts.instanceUrl, started, code);
|
|
122
|
+
if (res.status === 200 && res.data) {
|
|
123
|
+
const tokens = tokensFromAuthResponse(res.data);
|
|
124
|
+
if (!tokens) {
|
|
125
|
+
throw new LoginError("The instance returned an unexpected verification response.");
|
|
126
|
+
}
|
|
127
|
+
const email = typeof res.data.email === "string" ? res.data.email : undefined;
|
|
128
|
+
const sub = typeof res.data.sub === "string" ? res.data.sub : started.sub;
|
|
129
|
+
return {
|
|
130
|
+
tokens,
|
|
131
|
+
identity: { sub, email, identifierType: channel },
|
|
132
|
+
channel,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (isRateLimited(res)) {
|
|
136
|
+
throw new LoginError("Too many attempts. The instance limits OTP to 10 per 15 minutes per IP. Wait and try again.");
|
|
137
|
+
}
|
|
138
|
+
attempt++;
|
|
139
|
+
notify({ type: "incorrect", attemptsLeft: maxAttempts - attempt });
|
|
140
|
+
}
|
|
141
|
+
throw new LoginError("Could not verify a code. Run seamless login to try again.");
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=loginFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loginFlow.js","sourceRoot":"","sources":["../../src/core/loginFlow.ts"],"names":[],"mappings":"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"}
|
package/dist/core/output.js
CHANGED
|
@@ -79,6 +79,52 @@ export function printSuccessOutput(config) {
|
|
|
79
79
|
console.log(kleur.dim("Docs: ") + kleur.cyan("https://docs.seamlessauth.com\n"));
|
|
80
80
|
console.log(kleur.bold().green("Setup complete.\n"));
|
|
81
81
|
}
|
|
82
|
+
export function printManagedSuccessOutput(config) {
|
|
83
|
+
const { projectName, webFramework, apiFramework, authServerUrl, appName } = config;
|
|
84
|
+
const title = kleur.bold().cyan("SEAMLESS");
|
|
85
|
+
console.log(`
|
|
86
|
+
╔════════════════════════════════════════╗
|
|
87
|
+
║ ${title} ║
|
|
88
|
+
╚════════════════════════════════════════╝
|
|
89
|
+
`);
|
|
90
|
+
console.log(kleur.green("Project connected to a managed instance.\n"));
|
|
91
|
+
console.log(kleur.dim("Managed application: ") + kleur.bold(appName));
|
|
92
|
+
console.log(kleur.dim("Auth server: ") + kleur.cyan(authServerUrl));
|
|
93
|
+
console.log("");
|
|
94
|
+
if (projectName) {
|
|
95
|
+
console.log(kleur.dim("Project directory: ") + kleur.bold(projectName));
|
|
96
|
+
console.log(kleur.cyan(`cd ${projectName}\n`));
|
|
97
|
+
}
|
|
98
|
+
console.log(kleur.bold("Included services:\n"));
|
|
99
|
+
if (webFramework) {
|
|
100
|
+
console.log(" • " +
|
|
101
|
+
kleur.white("Web application") +
|
|
102
|
+
kleur.dim(` (${formatFramework(webFramework)})`));
|
|
103
|
+
}
|
|
104
|
+
if (apiFramework) {
|
|
105
|
+
console.log(" • " +
|
|
106
|
+
kleur.white("API server") +
|
|
107
|
+
kleur.dim(` (${formatFramework(apiFramework)})`));
|
|
108
|
+
}
|
|
109
|
+
console.log(" • " + kleur.white("Auth server") + kleur.dim(" (managed instance)"));
|
|
110
|
+
console.log("");
|
|
111
|
+
console.log(kleur.bold("Next steps:\n"));
|
|
112
|
+
if (apiFramework) {
|
|
113
|
+
console.log(kleur.dim(" # API server"));
|
|
114
|
+
console.log(" cd api && npm install && npm run dev\n");
|
|
115
|
+
}
|
|
116
|
+
if (webFramework) {
|
|
117
|
+
console.log(kleur.dim(" # Web app"));
|
|
118
|
+
console.log(" cd web && npm install && npm run dev\n");
|
|
119
|
+
}
|
|
120
|
+
console.log(" Sign in from the web app to confirm the session resolves.\n");
|
|
121
|
+
console.log(kleur.bold("Notes:\n"));
|
|
122
|
+
console.log(kleur.dim(" • The API service token was written to api/.env. Keep it out of version control."));
|
|
123
|
+
console.log(kleur.dim(" • Auth, users, and OAuth providers are managed from the dashboard, not locally."));
|
|
124
|
+
console.log(kleur.dim(" • Rotate the service token anytime with the dashboard.\n"));
|
|
125
|
+
console.log(kleur.dim("Docs: ") + kleur.cyan("https://docs.seamlessauth.com\n"));
|
|
126
|
+
console.log(kleur.bold().green("Setup complete.\n"));
|
|
127
|
+
}
|
|
82
128
|
function formatFramework(name) {
|
|
83
129
|
const map = {
|
|
84
130
|
react: "React",
|