better-auth-studio 1.0.49-beta.1 → 1.0.49-beta.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA8I/E;AAwBD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA8uJR"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA8I/E;AAwBD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAyiKR"}
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHmac, randomBytes } from 'node:crypto';
2
- import { existsSync, readFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
5
  // @ts-expect-error
@@ -4302,22 +4302,17 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4302
4302
  let filename;
4303
4303
  let contentType;
4304
4304
  if (exportFormat === 'csv') {
4305
- // Convert to CSV
4306
4305
  const csvRows = [];
4307
4306
  for (const [tableName, rows] of Object.entries(exportData)) {
4308
4307
  if (rows.length === 0)
4309
4308
  continue;
4310
- // Add table header
4311
4309
  csvRows.push(`\n=== ${tableName.toUpperCase()} ===\n`);
4312
- // Get all unique keys from all rows
4313
4310
  const allKeys = new Set();
4314
4311
  rows.forEach((row) => {
4315
4312
  Object.keys(row).forEach((key) => allKeys.add(key));
4316
4313
  });
4317
4314
  const headers = Array.from(allKeys);
4318
- // Write CSV header
4319
4315
  csvRows.push(headers.map((h) => `"${h}"`).join(','));
4320
- // Write CSV rows
4321
4316
  rows.forEach((row) => {
4322
4317
  const values = headers.map((header) => {
4323
4318
  const value = row[header];
@@ -4335,7 +4330,6 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4335
4330
  contentType = 'text/csv';
4336
4331
  }
4337
4332
  else {
4338
- // JSON format
4339
4333
  output = JSON.stringify(exportData, null, 2);
4340
4334
  filename = `better-auth-export-${new Date().toISOString().split('T')[0]}.json`;
4341
4335
  contentType = 'application/json';
@@ -4487,6 +4481,290 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4487
4481
  });
4488
4482
  }
4489
4483
  });
4484
+ router.post('/api/tools/test-oauth-credentials', async (req, res) => {
4485
+ try {
4486
+ const { provider, clientId, clientSecret, redirectURI } = req.body || {};
4487
+ if (!provider || !clientId || !clientSecret) {
4488
+ return res.status(400).json({
4489
+ success: false,
4490
+ message: 'Provider, Client ID, and Client Secret are required',
4491
+ });
4492
+ }
4493
+ if (provider.toLowerCase() === 'google') {
4494
+ try {
4495
+ const tokenInfoUrl = `https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=test`;
4496
+ const isValidClientIdFormat = /^[\d\w-]+\.apps\.googleusercontent\.com$|^\d+$/.test(clientId);
4497
+ if (!isValidClientIdFormat) {
4498
+ return res.json({
4499
+ success: false,
4500
+ message: 'Invalid Google Client ID format',
4501
+ details: {
4502
+ provider: 'google',
4503
+ clientIdFormat: 'Should end with .apps.googleusercontent.com or be numeric',
4504
+ },
4505
+ });
4506
+ }
4507
+ const discoveryUrl = 'https://accounts.google.com/.well-known/openid-configuration';
4508
+ const discoveryResponse = await fetch(discoveryUrl);
4509
+ if (!discoveryResponse.ok) {
4510
+ return res.json({
4511
+ success: false,
4512
+ message: 'Unable to reach Google OAuth service',
4513
+ details: {
4514
+ provider: 'google',
4515
+ error: 'Network error',
4516
+ },
4517
+ });
4518
+ }
4519
+ if (redirectURI) {
4520
+ try {
4521
+ new URL(redirectURI);
4522
+ }
4523
+ catch {
4524
+ return res.json({
4525
+ success: false,
4526
+ message: 'Invalid Redirect URI format',
4527
+ details: {
4528
+ provider: 'google',
4529
+ redirectURI: redirectURI,
4530
+ },
4531
+ });
4532
+ }
4533
+ }
4534
+ return res.json({
4535
+ success: true,
4536
+ message: 'Google OAuth credentials format is valid',
4537
+ details: {
4538
+ provider: 'google',
4539
+ clientId: clientId.substring(0, 20) + '...',
4540
+ redirectURI: redirectURI || 'Not configured',
4541
+ note: 'Credentials format validated. Test with actual OAuth flow to confirm.',
4542
+ },
4543
+ });
4544
+ }
4545
+ catch (error) {
4546
+ return res.json({
4547
+ success: false,
4548
+ message: 'Failed to validate Google OAuth credentials',
4549
+ details: {
4550
+ provider: 'google',
4551
+ error: error instanceof Error ? error.message : 'Unknown error',
4552
+ },
4553
+ });
4554
+ }
4555
+ }
4556
+ if (!clientId.trim() || !clientSecret.trim()) {
4557
+ return res.json({
4558
+ success: false,
4559
+ message: 'Client ID and Secret cannot be empty',
4560
+ });
4561
+ }
4562
+ if (redirectURI) {
4563
+ try {
4564
+ new URL(redirectURI);
4565
+ }
4566
+ catch {
4567
+ return res.json({
4568
+ success: false,
4569
+ message: 'Invalid Redirect URI format',
4570
+ details: {
4571
+ provider: provider,
4572
+ redirectURI: redirectURI,
4573
+ },
4574
+ });
4575
+ }
4576
+ }
4577
+ return res.json({
4578
+ success: true,
4579
+ message: `${provider} OAuth credentials format is valid`,
4580
+ details: {
4581
+ provider: provider,
4582
+ clientId: clientId.substring(0, 20) + '...',
4583
+ redirectURI: redirectURI || 'Not configured',
4584
+ note: 'Credentials format validated. Test with actual OAuth flow to confirm.',
4585
+ },
4586
+ });
4587
+ }
4588
+ catch (error) {
4589
+ res.status(500).json({
4590
+ success: false,
4591
+ message: error instanceof Error ? error.message : 'Failed to test OAuth credentials',
4592
+ });
4593
+ }
4594
+ });
4595
+ router.post('/api/tools/generate-secret', async (_req, res) => {
4596
+ try {
4597
+ const { length = 32, format = 'hex' } = _req.body || {};
4598
+ const secretLength = typeof length === 'number' && length >= 16 && length <= 128 ? length : 32;
4599
+ const secretFormat = format === 'base64' ? 'base64' : 'hex';
4600
+ const secretBytes = randomBytes(secretLength);
4601
+ const secret = secretFormat === 'hex' ? secretBytes.toString('hex') : secretBytes.toString('base64');
4602
+ const entropy = secretLength * 8; // bits of entropy
4603
+ res.json({
4604
+ success: true,
4605
+ secret,
4606
+ format: secretFormat,
4607
+ length: secretLength,
4608
+ entropy,
4609
+ envFormat: `BETTER_AUTH_SECRET=${secret}`,
4610
+ });
4611
+ }
4612
+ catch (error) {
4613
+ res.status(500).json({
4614
+ success: false,
4615
+ message: error instanceof Error ? error.message : 'Failed to generate secret',
4616
+ });
4617
+ }
4618
+ });
4619
+ router.post('/api/tools/check-env-credentials', async (req, res) => {
4620
+ try {
4621
+ const { provider } = req.body || {};
4622
+ if (!provider) {
4623
+ return res.status(400).json({
4624
+ success: false,
4625
+ message: 'Provider is required',
4626
+ });
4627
+ }
4628
+ const envPath = join(process.cwd(), '.env');
4629
+ const envLocalPath = join(process.cwd(), '.env.local');
4630
+ // Try .env.local first, then .env
4631
+ const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
4632
+ const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
4633
+ // Generate environment variable names
4634
+ const providerUpper = provider.toUpperCase();
4635
+ const clientIdKey = `${providerUpper}_CLIENT_ID`;
4636
+ const clientSecretKey = `${providerUpper}_CLIENT_SECRET`;
4637
+ // Parse existing .env file
4638
+ const envLines = envContent.split('\n');
4639
+ const existingCredentials = {};
4640
+ envLines.forEach((line) => {
4641
+ const trimmed = line.trim();
4642
+ if (!trimmed || trimmed.startsWith('#'))
4643
+ return;
4644
+ const match = trimmed.match(/^([^=#]+)=(.*)$/);
4645
+ if (match) {
4646
+ const key = match[1].trim();
4647
+ const value = match[2].trim();
4648
+ if (key === clientIdKey || key === clientSecretKey) {
4649
+ existingCredentials[key] = value;
4650
+ }
4651
+ }
4652
+ });
4653
+ const hasExisting = existingCredentials[clientIdKey] || existingCredentials[clientSecretKey];
4654
+ res.json({
4655
+ success: true,
4656
+ hasExisting,
4657
+ existingCredentials: hasExisting ? existingCredentials : {},
4658
+ path: targetPath,
4659
+ });
4660
+ }
4661
+ catch (error) {
4662
+ res.status(500).json({
4663
+ success: false,
4664
+ message: error instanceof Error ? error.message : 'Failed to check credentials',
4665
+ });
4666
+ }
4667
+ });
4668
+ router.post('/api/tools/write-env-credentials', async (req, res) => {
4669
+ try {
4670
+ const { provider, clientId, clientSecret, action = 'override' } = req.body || {};
4671
+ if (!provider || !clientId || !clientSecret) {
4672
+ return res.status(400).json({
4673
+ success: false,
4674
+ message: 'Provider, Client ID, and Client Secret are required',
4675
+ });
4676
+ }
4677
+ const envPath = join(process.cwd(), '.env');
4678
+ const envLocalPath = join(process.cwd(), '.env.local');
4679
+ const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
4680
+ const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
4681
+ const envLines = envContent.split('\n');
4682
+ const envMap = new Map();
4683
+ const newLines = [];
4684
+ envLines.forEach((line, index) => {
4685
+ const trimmed = line.trim();
4686
+ if (!trimmed || trimmed.startsWith('#')) {
4687
+ newLines.push(line);
4688
+ return;
4689
+ }
4690
+ const match = trimmed.match(/^([^=#]+)=(.*)$/);
4691
+ if (match) {
4692
+ const key = match[1].trim();
4693
+ const value = match[2].trim();
4694
+ envMap.set(key, { line, index });
4695
+ newLines.push(line);
4696
+ }
4697
+ else {
4698
+ newLines.push(line);
4699
+ }
4700
+ });
4701
+ const providerUpper = provider.toUpperCase();
4702
+ let clientIdKey = `${providerUpper}_CLIENT_ID`;
4703
+ let clientSecretKey = `${providerUpper}_CLIENT_SECRET`;
4704
+ if (action === 'append') {
4705
+ let suffix = 2;
4706
+ while (envMap.has(clientIdKey) || envMap.has(clientSecretKey)) {
4707
+ clientIdKey = `${providerUpper}_CLIENT_ID_${suffix}`;
4708
+ clientSecretKey = `${providerUpper}_CLIENT_SECRET_${suffix}`;
4709
+ suffix++;
4710
+ }
4711
+ }
4712
+ let updated = false;
4713
+ if (action === 'override' && envMap.has(clientIdKey)) {
4714
+ const existing = envMap.get(clientIdKey);
4715
+ newLines[existing.index] = `${clientIdKey}=${clientId}`;
4716
+ updated = true;
4717
+ }
4718
+ else if (!envMap.has(clientIdKey)) {
4719
+ // Remove trailing empty lines
4720
+ while (newLines.length > 0 && !newLines[newLines.length - 1].trim()) {
4721
+ newLines.pop();
4722
+ }
4723
+ if (newLines.length > 0 &&
4724
+ newLines[newLines.length - 1] &&
4725
+ !newLines[newLines.length - 1].endsWith('\n')) {
4726
+ if (!newLines[newLines.length - 1].endsWith('\r\n') &&
4727
+ !newLines[newLines.length - 1].endsWith('\n')) {
4728
+ newLines.push('');
4729
+ }
4730
+ }
4731
+ newLines.push(`${clientIdKey}=${clientId}`);
4732
+ updated = true;
4733
+ }
4734
+ if (action === 'override' && envMap.has(clientSecretKey)) {
4735
+ const existing = envMap.get(clientSecretKey);
4736
+ newLines[existing.index] = `${clientSecretKey}=${clientSecret}`;
4737
+ updated = true;
4738
+ }
4739
+ else if (!envMap.has(clientSecretKey)) {
4740
+ const clientIdIndex = newLines.findIndex((line) => line.startsWith(`${clientIdKey}=`));
4741
+ if (clientIdIndex >= 0) {
4742
+ newLines.splice(clientIdIndex + 1, 0, `${clientSecretKey}=${clientSecret}`);
4743
+ }
4744
+ else {
4745
+ newLines.push(`${clientSecretKey}=${clientSecret}`);
4746
+ }
4747
+ updated = true;
4748
+ }
4749
+ const newContent = newLines.join('\n');
4750
+ writeFileSync(targetPath, newContent, 'utf-8');
4751
+ res.json({
4752
+ success: true,
4753
+ message: 'OAuth credentials written successfully',
4754
+ path: targetPath,
4755
+ variables: {
4756
+ [clientIdKey]: clientId,
4757
+ [clientSecretKey]: '***',
4758
+ },
4759
+ });
4760
+ }
4761
+ catch (error) {
4762
+ res.status(500).json({
4763
+ success: false,
4764
+ message: error instanceof Error ? error.message : 'Failed to write credentials to .env',
4765
+ });
4766
+ }
4767
+ });
4490
4768
  return router;
4491
4769
  }
4492
4770
  //# sourceMappingURL=routes.js.map