oauth-init 1.0.0 → 1.1.1

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 (3) hide show
  1. package/README.md +3 -0
  2. package/dist/index.js +391 -5
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -47,6 +47,9 @@ The CLI will:
47
47
  |----------|------------|
48
48
  | Google | OAuth 2.0 (External) |
49
49
  | GitHub | OAuth App, GitHub App |
50
+ | Discord | OAuth 2.0 |
51
+ | GitLab | OAuth 2.0 |
52
+ | Vercel | OAuth Token |
50
53
 
51
54
  ## Tech Stack
52
55
 
package/dist/index.js CHANGED
@@ -10221,6 +10221,350 @@ ${callbackUrl}`, "Save this URL");
10221
10221
  }
10222
10222
  }
10223
10223
 
10224
+ // src/lib/providers/vercel.ts
10225
+ init_dist2();
10226
+ function logStep5(message) {
10227
+ if (!globalConfig.quiet)
10228
+ R2.step(message);
10229
+ }
10230
+ function logInfo2(message) {
10231
+ if (!globalConfig.quiet)
10232
+ R2.info(message);
10233
+ }
10234
+ function logMessage5(message) {
10235
+ if (!globalConfig.quiet)
10236
+ R2.message(message);
10237
+ }
10238
+ async function checkVercelCLI() {
10239
+ const s = bt2();
10240
+ s.start("Checking Vercel CLI...");
10241
+ try {
10242
+ await execa("vercel", ["--version"]);
10243
+ } catch {
10244
+ s.stop("Vercel CLI not found.");
10245
+ R2.error(`Vercel CLI is required for Vercel OAuth setup.
10246
+ ` + `Install it: npm i -g vercel
10247
+ ` + "Then run: vercel login");
10248
+ return false;
10249
+ }
10250
+ s.stop("Vercel CLI found.");
10251
+ return true;
10252
+ }
10253
+ async function checkVercelAuth() {
10254
+ const authSpinner = bt2();
10255
+ authSpinner.start("Checking Vercel authentication...");
10256
+ try {
10257
+ const { stdout } = await execa("vercel", ["whoami"]);
10258
+ if (!stdout.trim()) {
10259
+ authSpinner.stop("Not authenticated.");
10260
+ return { authenticated: false };
10261
+ }
10262
+ authSpinner.stop(`Logged in as: ${stdout.trim()}`);
10263
+ return { authenticated: true, user: stdout.trim() };
10264
+ } catch {
10265
+ authSpinner.stop("Not authenticated.");
10266
+ return { authenticated: false };
10267
+ }
10268
+ }
10269
+ async function getVercelTeams() {
10270
+ const loadingSpinner = bt2();
10271
+ loadingSpinner.start("Fetching Vercel teams...");
10272
+ try {
10273
+ const { stdout, stderr } = await execa("vercel", ["teams", "ls", "--format", "json"]);
10274
+ const teamsJson = JSON.parse(stdout);
10275
+ const teams = teamsJson.teams.map((team) => ({
10276
+ name: team.name,
10277
+ slug: team.slug
10278
+ }));
10279
+ loadingSpinner.stop("Fetched teams.");
10280
+ return teams;
10281
+ } catch (stderr) {
10282
+ loadingSpinner.stop("No teams found or not a team member. " + stderr);
10283
+ return [];
10284
+ }
10285
+ }
10286
+
10287
+ class VercelAuthProvider {
10288
+ async run(_appName) {
10289
+ try {
10290
+ const isCLIInstalled = await checkVercelCLI();
10291
+ if (!isCLIInstalled) {
10292
+ R2.error("Please install Vercel CLI and authenticate before continuing.");
10293
+ process.exit(1);
10294
+ }
10295
+ const auth = await checkVercelAuth();
10296
+ if (!auth.authenticated) {
10297
+ R2.error("Please run: vercel login");
10298
+ process.exit(1);
10299
+ }
10300
+ const teams = await getVercelTeams();
10301
+ let selectedTeam;
10302
+ if (teams.length === 0) {
10303
+ R2.error("No teams found. Please ensure you have a Vercel team or personal account.");
10304
+ process.exit(1);
10305
+ } else if (teams.length === 1) {
10306
+ selectedTeam = teams[0];
10307
+ logInfo2(`Using team: ${selectedTeam.name}`);
10308
+ } else {
10309
+ const teamId = await Je({
10310
+ message: "Select your team",
10311
+ options: teams.map((team) => ({
10312
+ label: team.name,
10313
+ value: team.slug
10314
+ }))
10315
+ });
10316
+ selectedTeam = { name: teamId, slug: teamId };
10317
+ }
10318
+ const teamSlug = selectedTeam.slug;
10319
+ const appsUrl = teamSlug ? `https://vercel.com/${teamSlug}/~/settings/apps` : `https://vercel.com/settings/apps`;
10320
+ logStep5("Step 1: Create Vercel OAuth App");
10321
+ logMessage5("Create an App in your Vercel dashboard to get OAuth credentials.");
10322
+ Ve(`Required Authorization Callback URL:
10323
+ ${_appName}`, "Save this URL");
10324
+ if (!globalConfig.noOpen) {
10325
+ const shouldOpen = globalConfig.skipPrompts ? true : await Re({
10326
+ message: "Open Vercel Apps settings page?",
10327
+ initialValue: true
10328
+ });
10329
+ if (Ct(shouldOpen))
10330
+ return Ne("Setup aborted.");
10331
+ if (shouldOpen)
10332
+ await open_default(appsUrl);
10333
+ }
10334
+ if (!globalConfig.skipPrompts) {
10335
+ Ve(`1. Go to ${appsUrl}
10336
+ 2. Click 'Create' to create a new App
10337
+ 3. Enter Name and Slug for your app
10338
+ 4. Configure Authorization Callback URL (use the URL above)
10339
+ 5. Choose client authentication method
10340
+ 6. Click Save
10341
+ 7. Go to authentication tab, scroll down and generate secret`, "Action Required");
10342
+ await Ze({
10343
+ message: "Press Enter once you've created the app and generated a client secret (or type 'skip' if done previously)"
10344
+ });
10345
+ }
10346
+ logStep5("Step 2: Enter Vercel OAuth Credentials");
10347
+ let clientId;
10348
+ let clientSecret;
10349
+ if (globalConfig.skipPrompts) {
10350
+ R2.error("Client ID and Secret required in non-interactive mode. Run without --skip-prompts");
10351
+ process.exit(1);
10352
+ }
10353
+ clientId = await Ze({
10354
+ message: "Paste your Vercel Client ID:",
10355
+ placeholder: "your_client_id",
10356
+ validate: (value) => value && value.length > 0 ? undefined : "Client ID is required"
10357
+ });
10358
+ if (Ct(clientId))
10359
+ return Ne("Setup aborted.");
10360
+ clientSecret = await He({
10361
+ message: "Paste your Vercel Client Secret:"
10362
+ });
10363
+ if (Ct(clientSecret))
10364
+ return Ne("Setup aborted.");
10365
+ logStep5("Step 3: Save credentials");
10366
+ const saveOption = await askSaveOption();
10367
+ if (Ct(saveOption))
10368
+ return Ne("Setup aborted.");
10369
+ await saveCredentials(clientId, clientSecret, "vercel", saveOption);
10370
+ } catch (err) {
10371
+ R2.error(`Setup Failed: ${err.message}`);
10372
+ process.exit(1);
10373
+ }
10374
+ }
10375
+ }
10376
+
10377
+ // src/lib/providers/microsoft.ts
10378
+ init_dist2();
10379
+ function logStep6(message) {
10380
+ if (!globalConfig.quiet)
10381
+ R2.step(message);
10382
+ }
10383
+ function logInfo3(message) {
10384
+ if (!globalConfig.quiet)
10385
+ R2.info(message);
10386
+ }
10387
+ function logMessage6(message) {
10388
+ if (!globalConfig.quiet)
10389
+ R2.message(message);
10390
+ }
10391
+ var CREATE_NEW_MICROSOFT_APP_OPTION = "register_new_microsoft_app";
10392
+ async function loadMicrosoftApplications(startMessage = "Fetching azure applications") {
10393
+ const loading = bt2();
10394
+ loading.start(startMessage);
10395
+ const { stdout } = await execa("az", [
10396
+ "ad",
10397
+ "app",
10398
+ "list",
10399
+ "--query",
10400
+ "[].{name:displayName, appId:appId}",
10401
+ "-o",
10402
+ "json"
10403
+ ]);
10404
+ loading.stop("Fetched Applications");
10405
+ return JSON.parse(stdout);
10406
+ }
10407
+ async function checkAzureAuth() {
10408
+ const s = bt2();
10409
+ s.start("Checking azure CLI...");
10410
+ try {
10411
+ await execa("az", ["version"]);
10412
+ } catch {
10413
+ s.stop("azure CLI not found.");
10414
+ R2.error(`azure CLI is required for Microsoft OAuth setup.
10415
+ ` + `Install it: https://learn.microsoft.com/en-us/cli/azure/get-started-with-azure-cli?view=azure-cli-latest
10416
+ ` + "Then run: az login");
10417
+ return false;
10418
+ }
10419
+ s.stop("azure CLI found.");
10420
+ const authSpinner = bt2();
10421
+ authSpinner.start("Checking azure authentication and subscriptions...");
10422
+ try {
10423
+ const { stdout } = await execa("az", [
10424
+ "account",
10425
+ "list",
10426
+ "--query= [].{name:name,state:state,isDefault:isDefault}",
10427
+ "-o",
10428
+ "table"
10429
+ ]);
10430
+ if (!stdout.trim()) {
10431
+ authSpinner.stop("Not authenticated.");
10432
+ R2.error("Please run: az login");
10433
+ return false;
10434
+ }
10435
+ authSpinner.stop(`Authenticated as: ${stdout.trim()}`);
10436
+ return true;
10437
+ } catch {
10438
+ authSpinner.stop("Authentication check failed.");
10439
+ return false;
10440
+ }
10441
+ }
10442
+
10443
+ class MicrosoftAuthProvider {
10444
+ async run(_appName) {
10445
+ try {
10446
+ const isAuthenticated = await checkAzureAuth();
10447
+ if (!isAuthenticated) {
10448
+ R2.error("Please install azure CLI and authenticate before continuing.");
10449
+ process.exit(1);
10450
+ }
10451
+ let appsList = await loadMicrosoftApplications();
10452
+ if (!appsList || appsList.length === 0) {
10453
+ R2.error("No Azure applications found. Create one at https://entra.microsoft.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade");
10454
+ process.exit(1);
10455
+ }
10456
+ logStep6("Step 1: Select from existing applications or register a new application in Microsoft Entra Admin Center");
10457
+ const brandUrl = `https://entra.microsoft.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade`;
10458
+ let selectedAppId;
10459
+ while (!selectedAppId) {
10460
+ const options = [
10461
+ {
10462
+ label: "Register a new Microsoft Entra application",
10463
+ value: CREATE_NEW_MICROSOFT_APP_OPTION
10464
+ },
10465
+ ...appsList.map((app) => ({
10466
+ label: app.appId ? `${app.name} (Default)` : app.name,
10467
+ value: app.appId
10468
+ }))
10469
+ ];
10470
+ const selection = await Je({
10471
+ message: "Select your application",
10472
+ options
10473
+ });
10474
+ if (Ct(selection))
10475
+ return Ne("Setup aborted.");
10476
+ if (selection === CREATE_NEW_MICROSOFT_APP_OPTION) {
10477
+ logMessage6(`Microsoft Entra requires manual setup for personal projects.
10478
+ Opening: ${brandUrl}`);
10479
+ Ve(`Required Redirect URI:
10480
+ ${_appName}`, "Save this URL");
10481
+ if (!globalConfig.noOpen) {
10482
+ const shouldOpen = globalConfig.skipPrompts ? true : await Re({
10483
+ message: "Open Microsoft Entra Admin Center?",
10484
+ initialValue: true
10485
+ });
10486
+ if (Ct(shouldOpen))
10487
+ return Ne("Setup aborted.");
10488
+ if (shouldOpen)
10489
+ await open_default(brandUrl);
10490
+ }
10491
+ if (!globalConfig.skipPrompts) {
10492
+ Ve(`1. Click on New Registration
10493
+ 2. Fill App Name & Redirect URI
10494
+ 3. Click Register`, "Action Required");
10495
+ const brandDone = await Ze({
10496
+ message: "Press Enter once you've saved the Consent Screen (or type 'skip' if done previously)"
10497
+ });
10498
+ if (Ct(brandDone))
10499
+ return Ne("Setup aborted.");
10500
+ }
10501
+ appsList = await loadMicrosoftApplications("Refreshing azure applications");
10502
+ if (!appsList || appsList.length === 0) {
10503
+ R2.error("No Azure applications found. Create one at https://entra.microsoft.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade");
10504
+ process.exit(1);
10505
+ }
10506
+ continue;
10507
+ }
10508
+ if (typeof selection !== "string") {
10509
+ continue;
10510
+ }
10511
+ selectedAppId = selection;
10512
+ }
10513
+ if (!selectedAppId) {
10514
+ R2.error("No application selected.");
10515
+ process.exit(1);
10516
+ }
10517
+ const appId = selectedAppId;
10518
+ logInfo3(`Registered Apps: ${appId}`);
10519
+ logStep6("Step 2: Copy OAuth Application(client) ID");
10520
+ const clientUrl = `https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/appId/${appId}/`;
10521
+ logMessage6(`Opening: ${clientUrl}`);
10522
+ Ve(`Required Redirect URI:
10523
+ ${_appName}`, "Save this URL");
10524
+ if (!globalConfig.noOpen) {
10525
+ const shouldOpen = globalConfig.skipPrompts ? true : await Re({
10526
+ message: "Check overview of your registered application?",
10527
+ initialValue: true
10528
+ });
10529
+ if (Ct(shouldOpen))
10530
+ return Ne("Setup aborted.");
10531
+ if (shouldOpen)
10532
+ await open_default(clientUrl);
10533
+ }
10534
+ if (!globalConfig.skipPrompts) {
10535
+ Ve(`1. Copy Application (Client) ID from overview
10536
+ 2. Create a new client secret, Under Manage
10537
+ 3. Select 'Certificates & Secrets' -> 'New client secret' -> Copy the value (save the secret when created as it won't be shown again)`, "Action Required");
10538
+ }
10539
+ let clientId;
10540
+ let clientSecret;
10541
+ if (globalConfig.skipPrompts) {
10542
+ R2.error("Client ID and Secret required in non-interactive mode. Run without --skip-prompts");
10543
+ process.exit(1);
10544
+ }
10545
+ clientId = await Ze({
10546
+ message: "Paste your Client ID:",
10547
+ placeholder: "2893e9ee-8956-4825-9923-84b21943bb24"
10548
+ });
10549
+ if (Ct(clientId))
10550
+ return Ne("Setup aborted.");
10551
+ clientSecret = await He({
10552
+ message: "Paste your Client Secret:"
10553
+ });
10554
+ if (Ct(clientSecret))
10555
+ return Ne("Setup aborted.");
10556
+ logStep6("Step 3: Save credentials");
10557
+ const saveOption = await askSaveOption();
10558
+ if (Ct(saveOption))
10559
+ return Ne("Setup aborted.");
10560
+ await saveCredentials(clientId, clientSecret, "microsoft", saveOption);
10561
+ } catch (err) {
10562
+ R2.error(`Setup Failed: ${err.message}`);
10563
+ process.exit(1);
10564
+ }
10565
+ }
10566
+ }
10567
+
10224
10568
  // src/index.ts
10225
10569
  var AUTH_LIBRARIES = [
10226
10570
  { name: "next-auth", callbackPattern: "/api/auth/callback/[provider]" },
@@ -10280,7 +10624,21 @@ GitLab OAuth Setup:
10280
10624
  1. Requires GitLab.com or GitLab Self-Managed
10281
10625
  2. Redirect URI: http://localhost:3000/api/auth/callback/gitlab
10282
10626
  3. Need: Application ID and Client Secret from GitLab Applications
10283
- 4. Supports user-owned, group-owned, or instance-wide apps`
10627
+ 4. Supports user-owned, group-owned, or instance-wide apps`,
10628
+ vercel: `
10629
+ Vercel OAuth Setup:
10630
+ 1. Uses Vercel CLI to list your teams and open the correct settings page
10631
+ 2. Requires: npm i -g vercel and vercel login
10632
+ 3. Configure Authorization Callback URL: http://localhost:3000/api/auth/callback/vercel
10633
+ 4. Generate a Client Secret in your app settings
10634
+ 5. Get the Client ID from your app settings
10635
+ 6. For better-auth: https://www.better-auth.com/docs/plugins/oauth#vercel`,
10636
+ microsoft: `
10637
+ Microsoft OAuth Setup:
10638
+ 1. Requires a registerd account on Microsoft Entra admin center for app registration
10639
+ 2. Redirect URI: http://localhost:3000/api/auth/callback/microsoft
10640
+ 3. Need: Client ID and Client Secret from the application's Overview Page
10641
+ 4. Setup involves creating an app registration, configuring API permissions and creating a client secret. For detailed steps, refer to the Microsoft provider documentation.`
10284
10642
  };
10285
10643
  function showProviderHelp(provider) {
10286
10644
  const help = PROVIDER_HELP[provider.toLowerCase()];
@@ -10356,6 +10714,32 @@ async function setupOAuthServices(oauthServices, customCallbackUrl) {
10356
10714
  }
10357
10715
  const gitlabProvider = new GitLabAuthProvider;
10358
10716
  await gitlabProvider.run(gitlabOauthCallback);
10717
+ } else if (service === "vercel") {
10718
+ R2.step("Vercel OAuth Setup");
10719
+ const vercelOauthCallback = globalConfig.skipPrompts ? defaultCallback : await Ze({
10720
+ message: "Enter the Vercel OAuth callback URL:",
10721
+ placeholder: defaultCallback,
10722
+ defaultValue: defaultCallback
10723
+ });
10724
+ if (Ct(vercelOauthCallback)) {
10725
+ Ne("Setup aborted.");
10726
+ return;
10727
+ }
10728
+ const vercelProvider = new VercelAuthProvider;
10729
+ await vercelProvider.run(vercelOauthCallback);
10730
+ } else if (service === "microsoft") {
10731
+ R2.step("Microsoft OAuth Setup");
10732
+ const microsoftOauthCallback = globalConfig.skipPrompts ? defaultCallback : await Ze({
10733
+ message: "Enter the Microsoft OAuth callback URL:",
10734
+ placeholder: defaultCallback,
10735
+ defaultValue: defaultCallback
10736
+ });
10737
+ if (Ct(microsoftOauthCallback)) {
10738
+ Ne("Setup aborted.");
10739
+ return;
10740
+ }
10741
+ const microsoftProvider = new MicrosoftAuthProvider;
10742
+ await microsoftProvider.run(microsoftOauthCallback);
10359
10743
  }
10360
10744
  }
10361
10745
  Le("OAuth setup completed! Thank you for using oauth-init!");
@@ -10371,7 +10755,7 @@ async function main() {
10371
10755
  callbackUrl: args.find((arg) => arg.startsWith("--callback-url="))?.split("=")[1] || args.find((arg) => arg.startsWith("-c="))?.split("=")[1]
10372
10756
  };
10373
10757
  const providerArg = args.find((arg) => !arg.startsWith("-"));
10374
- if (providerArg && (providerArg === "google" || providerArg === "github" || providerArg === "discord" || providerArg === "gitlab")) {
10758
+ if (providerArg && (providerArg === "google" || providerArg === "github" || providerArg === "discord" || providerArg === "gitlab" || providerArg === "vercel")) {
10375
10759
  showProviderHelp(providerArg);
10376
10760
  }
10377
10761
  if (flags.help) {
@@ -10383,7 +10767,7 @@ Options:
10383
10767
  -q, --quiet Reduce output verbosity
10384
10768
  -n, --no-open Don't open browser URLs automatically
10385
10769
  -y, --skip-prompts Use default options (for CI/CD)
10386
- -p, --provider= Specify providers (comma-separated): google,github,discord
10770
+ -p, --provider= Specify providers (comma-separated): google,github,discord,gitlab,vercel
10387
10771
  -c, --callback-url= Base callback URL (default: http://localhost:3000)
10388
10772
 
10389
10773
  Examples:
@@ -10398,7 +10782,7 @@ Examples:
10398
10782
  process.exit(0);
10399
10783
  }
10400
10784
  if (flags.provider) {
10401
- const validProviders = ["google", "github", "discord", "gitlab"];
10785
+ const validProviders = ["google", "github", "discord", "gitlab", "vercel", "microsoft"];
10402
10786
  const providers = flags.provider.split(",").map((p) => p.trim().toLowerCase());
10403
10787
  const invalid = providers.filter((p) => !validProviders.includes(p));
10404
10788
  if (invalid.length > 0) {
@@ -10441,7 +10825,9 @@ Examples:
10441
10825
  { value: "google", label: "Google" },
10442
10826
  { value: "github", label: "Github" },
10443
10827
  { value: "discord", label: "Discord" },
10444
- { value: "gitlab", label: "GitLab" }
10828
+ { value: "gitlab", label: "GitLab" },
10829
+ { value: "vercel", label: "Vercel" },
10830
+ { value: "microsoft", label: "Microsoft" }
10445
10831
  ]
10446
10832
  });
10447
10833
  if (Ct(oauthToSetup)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth-init",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "CLI for setting up OAuth providers for your project",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,7 +27,9 @@
27
27
  "setup",
28
28
  "interactive",
29
29
  "bun",
30
- "typescript"
30
+ "typescript",
31
+ "vercel",
32
+ "gitlab"
31
33
  ],
32
34
  "dependencies": {
33
35
  "@clack/prompts": "^1.0.1",