@better-auth/cli 1.3.8-beta.2 → 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.
Files changed (2) hide show
  1. package/dist/index.mjs +207 -1
  2. package/package.json +5 -2
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) {
@@ -2792,6 +2796,208 @@ const generate = new Command("generate").option(
2792
2796
  "the path to the configuration file. defaults to the first configuration file found."
2793
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);
2794
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
+
2795
3001
  process.on("SIGINT", () => process.exit(0));
2796
3002
  process.on("SIGTERM", () => process.exit(0));
2797
3003
  async function main() {
@@ -2801,7 +3007,7 @@ async function main() {
2801
3007
  packageInfo = await getPackageInfo();
2802
3008
  } catch (error) {
2803
3009
  }
2804
- 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());
2805
3011
  program.parse();
2806
3012
  }
2807
3013
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/cli",
3
- "version": "1.3.8-beta.2",
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"
@@ -49,13 +50,14 @@
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.2"
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
  }