better-auth-studio 1.0.49-beta.1 → 1.0.49-beta.11
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/routes.d.ts.map +1 -1
- package/dist/routes.js +285 -7
- package/dist/routes.js.map +1 -1
- package/dist/studio.d.ts.map +1 -1
- package/dist/studio.js +11 -3
- package/dist/studio.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/main-CvEhI3bC.css +1 -0
- package/public/assets/main-DGjVB3rc.js +1090 -0
- package/public/index.html +2 -2
- package/public/assets/main-BxeUnfRQ.js +0 -1090
- package/public/assets/main-nQqgTBjq.css +0 -1
package/dist/routes.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|