@better-auth/cli 1.3.8-beta.1 → 1.3.8-beta.3
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/index.mjs +213 -4
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
|
@@ -20,6 +20,10 @@ import { loadConfig } from 'c12';
|
|
|
20
20
|
import babelPresetTypeScript from '@babel/preset-typescript';
|
|
21
21
|
import babelPresetReact from '@babel/preset-react';
|
|
22
22
|
import { produceSchema } from '@mrleebo/prisma-ast';
|
|
23
|
+
import { createAuthClient } from 'better-auth/client';
|
|
24
|
+
import { deviceAuthorizationClient } from 'better-auth/client/plugins';
|
|
25
|
+
import open from 'open';
|
|
26
|
+
import os from 'os';
|
|
23
27
|
import 'dotenv/config';
|
|
24
28
|
|
|
25
29
|
function getPackageInfo(cwd) {
|
|
@@ -1957,6 +1961,9 @@ const jitiOptions = (cwd) => {
|
|
|
1957
1961
|
alias
|
|
1958
1962
|
};
|
|
1959
1963
|
};
|
|
1964
|
+
const isDefaultExport = (object) => {
|
|
1965
|
+
return typeof object === "object" && object !== null && !Array.isArray(object) && Object.keys(object).length > 0 && "options" in object;
|
|
1966
|
+
};
|
|
1960
1967
|
async function getConfig({
|
|
1961
1968
|
cwd,
|
|
1962
1969
|
configPath,
|
|
@@ -1972,7 +1979,7 @@ async function getConfig({
|
|
|
1972
1979
|
dotenv: true,
|
|
1973
1980
|
jitiOptions: jitiOptions(cwd)
|
|
1974
1981
|
});
|
|
1975
|
-
if (!
|
|
1982
|
+
if (!("auth" in config) && !isDefaultExport(config)) {
|
|
1976
1983
|
if (shouldThrowOnError) {
|
|
1977
1984
|
throw new Error(
|
|
1978
1985
|
`Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`
|
|
@@ -1983,7 +1990,7 @@ async function getConfig({
|
|
|
1983
1990
|
);
|
|
1984
1991
|
process.exit(1);
|
|
1985
1992
|
}
|
|
1986
|
-
configFile = config.auth?.options
|
|
1993
|
+
configFile = "auth" in config ? config.auth?.options : config.options;
|
|
1987
1994
|
}
|
|
1988
1995
|
if (!configFile) {
|
|
1989
1996
|
for (const possiblePath of possiblePaths) {
|
|
@@ -2326,7 +2333,7 @@ const generateDrizzleSchema = async ({
|
|
|
2326
2333
|
${Object.keys(fields).map((field) => {
|
|
2327
2334
|
const attr = fields[field];
|
|
2328
2335
|
let type = getType(field, attr);
|
|
2329
|
-
if (attr.defaultValue) {
|
|
2336
|
+
if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") {
|
|
2330
2337
|
if (typeof attr.defaultValue === "function") {
|
|
2331
2338
|
type += `.$defaultFn(${attr.defaultValue})`;
|
|
2332
2339
|
} else if (typeof attr.defaultValue === "string") {
|
|
@@ -2789,6 +2796,208 @@ const generate = new Command("generate").option(
|
|
|
2789
2796
|
"the path to the configuration file. defaults to the first configuration file found."
|
|
2790
2797
|
).option("--output <output>", "the file to output to the generated schema").option("-y, --yes", "automatically answer yes to all prompts", false).option("--y", "(deprecated) same as --yes", false).action(generateAction);
|
|
2791
2798
|
|
|
2799
|
+
const DEMO_URL = "https://demo.better-auth.com";
|
|
2800
|
+
const CLIENT_ID = "better-auth-cli";
|
|
2801
|
+
const CONFIG_DIR = path.join(os.homedir(), ".better-auth");
|
|
2802
|
+
const TOKEN_FILE = path.join(CONFIG_DIR, "token.json");
|
|
2803
|
+
async function loginAction(opts) {
|
|
2804
|
+
const options = z.object({
|
|
2805
|
+
serverUrl: z.string().optional(),
|
|
2806
|
+
clientId: z.string().optional()
|
|
2807
|
+
}).parse(opts);
|
|
2808
|
+
const serverUrl = options.serverUrl || DEMO_URL;
|
|
2809
|
+
const clientId = options.clientId || CLIENT_ID;
|
|
2810
|
+
intro(chalk.bold("\u{1F510} Better Auth CLI Login (Demo)"));
|
|
2811
|
+
console.log(
|
|
2812
|
+
chalk.yellow(
|
|
2813
|
+
"\u26A0\uFE0F This is a demo feature for testing device authorization flow."
|
|
2814
|
+
)
|
|
2815
|
+
);
|
|
2816
|
+
console.log(
|
|
2817
|
+
chalk.gray(
|
|
2818
|
+
" It connects to the Better Auth demo server for testing purposes.\n"
|
|
2819
|
+
)
|
|
2820
|
+
);
|
|
2821
|
+
const existingToken = await getStoredToken();
|
|
2822
|
+
if (existingToken) {
|
|
2823
|
+
const shouldReauth = await confirm({
|
|
2824
|
+
message: "You're already logged in. Do you want to log in again?",
|
|
2825
|
+
initialValue: false
|
|
2826
|
+
});
|
|
2827
|
+
if (isCancel(shouldReauth) || !shouldReauth) {
|
|
2828
|
+
cancel("Login cancelled");
|
|
2829
|
+
process.exit(0);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
const authClient = createAuthClient({
|
|
2833
|
+
baseURL: serverUrl,
|
|
2834
|
+
plugins: [deviceAuthorizationClient()]
|
|
2835
|
+
});
|
|
2836
|
+
const spinner = yoctoSpinner({ text: "Requesting device authorization..." });
|
|
2837
|
+
spinner.start();
|
|
2838
|
+
try {
|
|
2839
|
+
const { data, error } = await authClient.device.code({
|
|
2840
|
+
client_id: clientId,
|
|
2841
|
+
scope: "openid profile email"
|
|
2842
|
+
});
|
|
2843
|
+
spinner.stop();
|
|
2844
|
+
if (error || !data) {
|
|
2845
|
+
logger.error(
|
|
2846
|
+
`Failed to request device authorization: ${error?.error_description || "Unknown error"}`
|
|
2847
|
+
);
|
|
2848
|
+
process.exit(1);
|
|
2849
|
+
}
|
|
2850
|
+
const {
|
|
2851
|
+
device_code,
|
|
2852
|
+
user_code,
|
|
2853
|
+
verification_uri,
|
|
2854
|
+
verification_uri_complete,
|
|
2855
|
+
interval = 5,
|
|
2856
|
+
expires_in
|
|
2857
|
+
} = data;
|
|
2858
|
+
console.log("");
|
|
2859
|
+
console.log(chalk.cyan("\u{1F4F1} Device Authorization Required"));
|
|
2860
|
+
console.log("");
|
|
2861
|
+
console.log(`Please visit: ${chalk.underline.blue(verification_uri)}`);
|
|
2862
|
+
console.log(`Enter code: ${chalk.bold.green(user_code)}`);
|
|
2863
|
+
console.log("");
|
|
2864
|
+
const shouldOpen = await confirm({
|
|
2865
|
+
message: "Open browser automatically?",
|
|
2866
|
+
initialValue: true
|
|
2867
|
+
});
|
|
2868
|
+
if (!isCancel(shouldOpen) && shouldOpen) {
|
|
2869
|
+
const urlToOpen = verification_uri_complete || verification_uri;
|
|
2870
|
+
await open(urlToOpen);
|
|
2871
|
+
}
|
|
2872
|
+
console.log(
|
|
2873
|
+
chalk.gray(
|
|
2874
|
+
`Waiting for authorization (expires in ${Math.floor(expires_in / 60)} minutes)...`
|
|
2875
|
+
)
|
|
2876
|
+
);
|
|
2877
|
+
const token = await pollForToken(
|
|
2878
|
+
authClient,
|
|
2879
|
+
device_code,
|
|
2880
|
+
clientId,
|
|
2881
|
+
interval
|
|
2882
|
+
);
|
|
2883
|
+
if (token) {
|
|
2884
|
+
await storeToken(token);
|
|
2885
|
+
const { data: session } = await authClient.getSession({
|
|
2886
|
+
fetchOptions: {
|
|
2887
|
+
headers: {
|
|
2888
|
+
Authorization: `Bearer ${token.access_token}`
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
});
|
|
2892
|
+
outro(
|
|
2893
|
+
chalk.green(
|
|
2894
|
+
`\u2705 Demo login successful! Logged in as ${session?.user?.name || session?.user?.email || "User"}`
|
|
2895
|
+
)
|
|
2896
|
+
);
|
|
2897
|
+
console.log(
|
|
2898
|
+
chalk.gray(
|
|
2899
|
+
"\n\u{1F4DD} Note: This was a demo authentication for testing purposes."
|
|
2900
|
+
)
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
} catch (err) {
|
|
2904
|
+
spinner.stop();
|
|
2905
|
+
logger.error(
|
|
2906
|
+
`Login failed: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
2907
|
+
);
|
|
2908
|
+
process.exit(1);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
|
|
2912
|
+
let pollingInterval = initialInterval;
|
|
2913
|
+
const spinner = yoctoSpinner({ text: "", color: "cyan" });
|
|
2914
|
+
let dots = 0;
|
|
2915
|
+
return new Promise((resolve, reject) => {
|
|
2916
|
+
const poll = async () => {
|
|
2917
|
+
dots = (dots + 1) % 4;
|
|
2918
|
+
spinner.text = chalk.gray(
|
|
2919
|
+
`Polling for authorization${".".repeat(dots)}${" ".repeat(3 - dots)}`
|
|
2920
|
+
);
|
|
2921
|
+
if (!spinner.isSpinning) spinner.start();
|
|
2922
|
+
try {
|
|
2923
|
+
const { data, error } = await authClient.device.token({
|
|
2924
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
2925
|
+
device_code: deviceCode,
|
|
2926
|
+
client_id: clientId,
|
|
2927
|
+
fetchOptions: {
|
|
2928
|
+
headers: {
|
|
2929
|
+
"user-agent": `Better Auth CLI`
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
});
|
|
2933
|
+
if (data?.access_token) {
|
|
2934
|
+
spinner.stop();
|
|
2935
|
+
resolve(data);
|
|
2936
|
+
return;
|
|
2937
|
+
} else if (error) {
|
|
2938
|
+
switch (error.error) {
|
|
2939
|
+
case "authorization_pending":
|
|
2940
|
+
break;
|
|
2941
|
+
case "slow_down":
|
|
2942
|
+
pollingInterval += 5;
|
|
2943
|
+
spinner.text = chalk.yellow(
|
|
2944
|
+
`Slowing down polling to ${pollingInterval}s`
|
|
2945
|
+
);
|
|
2946
|
+
break;
|
|
2947
|
+
case "access_denied":
|
|
2948
|
+
spinner.stop();
|
|
2949
|
+
logger.error("Access was denied by the user");
|
|
2950
|
+
process.exit(1);
|
|
2951
|
+
break;
|
|
2952
|
+
case "expired_token":
|
|
2953
|
+
spinner.stop();
|
|
2954
|
+
logger.error("The device code has expired. Please try again.");
|
|
2955
|
+
process.exit(1);
|
|
2956
|
+
break;
|
|
2957
|
+
default:
|
|
2958
|
+
spinner.stop();
|
|
2959
|
+
logger.error(`Error: ${error.error_description}`);
|
|
2960
|
+
process.exit(1);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
} catch (err) {
|
|
2964
|
+
spinner.stop();
|
|
2965
|
+
logger.error(
|
|
2966
|
+
`Network error: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
2967
|
+
);
|
|
2968
|
+
process.exit(1);
|
|
2969
|
+
}
|
|
2970
|
+
setTimeout(poll, pollingInterval * 1e3);
|
|
2971
|
+
};
|
|
2972
|
+
setTimeout(poll, pollingInterval * 1e3);
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
async function storeToken(token) {
|
|
2976
|
+
try {
|
|
2977
|
+
await fs$1.mkdir(CONFIG_DIR, { recursive: true });
|
|
2978
|
+
const tokenData = {
|
|
2979
|
+
access_token: token.access_token,
|
|
2980
|
+
token_type: token.token_type || "Bearer",
|
|
2981
|
+
scope: token.scope,
|
|
2982
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2983
|
+
};
|
|
2984
|
+
await fs$1.writeFile(TOKEN_FILE, JSON.stringify(tokenData, null, 2), "utf-8");
|
|
2985
|
+
} catch (error) {
|
|
2986
|
+
logger.warn("Failed to store authentication token locally");
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
async function getStoredToken() {
|
|
2990
|
+
try {
|
|
2991
|
+
const data = await fs$1.readFile(TOKEN_FILE, "utf-8");
|
|
2992
|
+
return JSON.parse(data);
|
|
2993
|
+
} catch {
|
|
2994
|
+
return null;
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
const login = new Command("login").description(
|
|
2998
|
+
"Demo: Test device authorization flow with Better Auth demo server"
|
|
2999
|
+
).option("--server-url <url>", "The Better Auth server URL", DEMO_URL).option("--client-id <id>", "The OAuth client ID", CLIENT_ID).action(loginAction);
|
|
3000
|
+
|
|
2792
3001
|
process.on("SIGINT", () => process.exit(0));
|
|
2793
3002
|
process.on("SIGTERM", () => process.exit(0));
|
|
2794
3003
|
async function main() {
|
|
@@ -2798,7 +3007,7 @@ async function main() {
|
|
|
2798
3007
|
packageInfo = await getPackageInfo();
|
|
2799
3008
|
} catch (error) {
|
|
2800
3009
|
}
|
|
2801
|
-
program.addCommand(init).addCommand(migrate).addCommand(generate).addCommand(generateSecret).version(packageInfo.version || "1.1.2").description("Better Auth CLI").action(() => program.help());
|
|
3010
|
+
program.addCommand(init).addCommand(migrate).addCommand(generate).addCommand(generateSecret).addCommand(login).version(packageInfo.version || "1.1.2").description("Better Auth CLI").action(() => program.help());
|
|
2802
3011
|
program.parse();
|
|
2803
3012
|
}
|
|
2804
3013
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/cli",
|
|
3
|
-
"version": "1.3.8-beta.
|
|
3
|
+
"version": "1.3.8-beta.3",
|
|
4
4
|
"description": "The CLI for Better Auth",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/diff": "^7.0.1",
|
|
30
30
|
"@types/fs-extra": "^11.0.4",
|
|
31
|
+
"tsx": "^4.20.4",
|
|
31
32
|
"typescript": "^5.9.2",
|
|
32
33
|
"unbuild": "^3.5.0",
|
|
33
34
|
"zod": "^4.0.0"
|
|
@@ -42,20 +43,21 @@
|
|
|
42
43
|
"@types/better-sqlite3": "^7.6.12",
|
|
43
44
|
"@types/prompts": "^2.4.9",
|
|
44
45
|
"better-sqlite3": "^11.6.0",
|
|
45
|
-
"c12": "^2.0
|
|
46
|
+
"c12": "^3.2.0",
|
|
46
47
|
"chalk": "^5.3.0",
|
|
47
48
|
"commander": "^12.1.0",
|
|
48
49
|
"dotenv": "^16.4.7",
|
|
49
50
|
"drizzle-orm": "^0.33.0",
|
|
50
51
|
"fs-extra": "^11.3.0",
|
|
51
52
|
"get-tsconfig": "^4.8.1",
|
|
53
|
+
"open": "^10.1.0",
|
|
52
54
|
"prettier": "^3.4.2",
|
|
53
55
|
"prisma": "^5.22.0",
|
|
54
56
|
"prompts": "^2.4.2",
|
|
55
57
|
"semver": "^7.7.1",
|
|
56
58
|
"tinyexec": "^0.3.1",
|
|
57
59
|
"yocto-spinner": "^0.1.1",
|
|
58
|
-
"better-auth": "1.3.8-beta.
|
|
60
|
+
"better-auth": "1.3.8-beta.3"
|
|
59
61
|
},
|
|
60
62
|
"peerDependencies": {
|
|
61
63
|
"zod": "3.25.0 || ^4.0.0"
|
|
@@ -67,6 +69,7 @@
|
|
|
67
69
|
"build": "unbuild",
|
|
68
70
|
"stub": "unbuild --stub",
|
|
69
71
|
"start": "node ./dist/index.mjs",
|
|
72
|
+
"dev": "tsx ./src/index.ts",
|
|
70
73
|
"test": "vitest"
|
|
71
74
|
}
|
|
72
75
|
}
|