swallowkit 1.0.0-beta.53 → 1.0.0-beta.55
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/cli/commands/scaffold.d.ts +6 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +9 -0
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts +1 -1
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +84 -25
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +4 -0
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +20 -0
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/bff-swa-auth.test.ts +275 -0
- package/src/__tests__/multi-auth.test.ts +18 -3
- package/src/__tests__/scaffold.test.ts +112 -1
- package/src/__tests__/ui-generator.test.ts +36 -0
- package/src/cli/commands/scaffold.ts +10 -2
- package/src/core/scaffold/auth-generator.ts +95 -33
- package/src/core/scaffold/ui-generator.ts +21 -0
|
@@ -1439,60 +1439,122 @@ export async function callFunction<TInput = any, TOutput = any>(
|
|
|
1439
1439
|
`;
|
|
1440
1440
|
}
|
|
1441
1441
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
let principal: string | null = null;
|
|
1456
|
-
{
|
|
1442
|
+
function generateBFFSwaCredentialForwarding(includeBearer: boolean): string {
|
|
1443
|
+
const bearerForwarding = includeBearer
|
|
1444
|
+
? ` const authorization = reqHeaders.get('authorization');
|
|
1445
|
+
if (authorization?.startsWith('Bearer ')) {
|
|
1446
|
+
fetchHeaders.Authorization = authorization;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
`
|
|
1450
|
+
: "";
|
|
1451
|
+
|
|
1452
|
+
return `${bearerForwarding} let principal = reqHeaders.get('x-ms-client-principal');
|
|
1453
|
+
|
|
1454
|
+
if (!principal) {
|
|
1457
1455
|
const cookie = reqHeaders.get('cookie');
|
|
1458
|
-
const
|
|
1456
|
+
const forwardedHost = reqHeaders.get('x-forwarded-host');
|
|
1457
|
+
const requestHost = reqHeaders.get('host');
|
|
1458
|
+
const host = (forwardedHost || requestHost)?.split(',')[0].trim();
|
|
1459
|
+
|
|
1459
1460
|
if (cookie && host) {
|
|
1460
|
-
const
|
|
1461
|
+
const forwardedProto = reqHeaders.get('x-forwarded-proto');
|
|
1462
|
+
const proto =
|
|
1463
|
+
forwardedProto?.split(',')[0].trim() ||
|
|
1464
|
+
(isLocalFunctionsUrl(functionsBaseUrl) ? 'http' : 'https');
|
|
1465
|
+
|
|
1461
1466
|
const meResponse = await fetch(\`\${proto}://\${host}/.auth/me\`, {
|
|
1462
1467
|
headers: { cookie },
|
|
1463
1468
|
cache: 'no-store',
|
|
1464
1469
|
});
|
|
1470
|
+
|
|
1465
1471
|
if (meResponse.ok) {
|
|
1466
|
-
const payload = await meResponse.json() as {
|
|
1472
|
+
const payload = (await meResponse.json()) as {
|
|
1473
|
+
clientPrincipal?: unknown;
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1467
1476
|
if (payload.clientPrincipal) {
|
|
1468
|
-
principal = Buffer.from(
|
|
1477
|
+
principal = Buffer.from(
|
|
1478
|
+
JSON.stringify(payload.clientPrincipal),
|
|
1479
|
+
'utf8'
|
|
1480
|
+
).toString('base64');
|
|
1469
1481
|
}
|
|
1470
1482
|
}
|
|
1471
1483
|
}
|
|
1472
1484
|
}
|
|
1485
|
+
|
|
1473
1486
|
if (principal) {
|
|
1474
|
-
const
|
|
1487
|
+
const decodedPrincipal = Buffer.from(principal, 'base64');
|
|
1488
|
+
if (decodedPrincipal.toString('base64') !== principal) {
|
|
1489
|
+
throw new Error('Invalid SWA client principal encoding');
|
|
1490
|
+
}
|
|
1491
|
+
const parsed = JSON.parse(
|
|
1492
|
+
decodedPrincipal.toString('utf8')
|
|
1493
|
+
) as Record<string, unknown>;
|
|
1494
|
+
|
|
1475
1495
|
const normalized = {
|
|
1476
|
-
identityProvider:
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1496
|
+
identityProvider:
|
|
1497
|
+
typeof parsed.identityProvider === 'string'
|
|
1498
|
+
? parsed.identityProvider
|
|
1499
|
+
: '',
|
|
1500
|
+
userId: typeof parsed.userId === 'string' ? parsed.userId : '',
|
|
1501
|
+
userDetails:
|
|
1502
|
+
typeof parsed.userDetails === 'string'
|
|
1503
|
+
? parsed.userDetails
|
|
1504
|
+
: '',
|
|
1505
|
+
userRoles: Array.isArray(parsed.userRoles)
|
|
1506
|
+
? parsed.userRoles.filter(
|
|
1507
|
+
(role: unknown): role is string => typeof role === 'string'
|
|
1508
|
+
)
|
|
1509
|
+
: [],
|
|
1480
1510
|
};
|
|
1481
|
-
|
|
1482
|
-
|
|
1511
|
+
|
|
1512
|
+
if (
|
|
1513
|
+
normalized.identityProvider &&
|
|
1514
|
+
normalized.userId &&
|
|
1515
|
+
normalized.userRoles.includes('authenticated')
|
|
1516
|
+
) {
|
|
1517
|
+
fetchHeaders['x-ms-client-principal'] = Buffer.from(
|
|
1518
|
+
JSON.stringify(normalized),
|
|
1519
|
+
'utf8'
|
|
1520
|
+
).toString('base64');
|
|
1483
1521
|
}
|
|
1484
|
-
}
|
|
1522
|
+
}`;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function generateBFFCallFunctionWithSwaCredentials(includeBearer: boolean): string {
|
|
1526
|
+
return generateBFFCallFunctionWithAuth()
|
|
1527
|
+
.replace(
|
|
1528
|
+
` // Authorization ヘッダーの転送(Proxy が cookie → Authorization に変換済み)`,
|
|
1529
|
+
` // Forward only validated credentials from the current request.`
|
|
1530
|
+
)
|
|
1531
|
+
.replace(
|
|
1532
|
+
` const authorization = reqHeaders.get('authorization');
|
|
1533
|
+
if (authorization) {
|
|
1534
|
+
fetchHeaders['Authorization'] = authorization;
|
|
1535
|
+
}`,
|
|
1536
|
+
generateBFFSwaCredentialForwarding(includeBearer)
|
|
1537
|
+
)
|
|
1538
|
+
.replace(
|
|
1539
|
+
` } catch {
|
|
1540
|
+
// headers() が使えないコンテキスト(ISR 等)では無視
|
|
1541
|
+
}`,
|
|
1542
|
+
` } catch (error) {
|
|
1543
|
+
// Do not include credentials or request header values in authentication logs.
|
|
1544
|
+
const errorType = error instanceof Error ? error.name : 'UnknownError';
|
|
1545
|
+
console.error('[BFF] Authentication forwarding failed:', errorType);
|
|
1546
|
+
}`
|
|
1485
1547
|
);
|
|
1486
1548
|
}
|
|
1487
1549
|
|
|
1550
|
+
/** SWA built-in auth variant: forward only a validated SWA client principal. */
|
|
1551
|
+
export function generateBFFCallFunctionWithSwaAuth(): string {
|
|
1552
|
+
return generateBFFCallFunctionWithSwaCredentials(false);
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1488
1555
|
/** Combined transport for named schemes. Functions still performs all verification. */
|
|
1489
1556
|
export function generateBFFCallFunctionWithMultipleAuth(): string {
|
|
1490
|
-
return
|
|
1491
|
-
` // Never forward a browser-supplied principal header. Resolve it from the SWA session endpoint.`,
|
|
1492
|
-
` const authorization = reqHeaders.get('authorization');
|
|
1493
|
-
if (authorization?.startsWith('Bearer ')) fetchHeaders['Authorization'] = authorization;
|
|
1494
|
-
// Never forward a browser-supplied principal header. Resolve it from the SWA session endpoint.`
|
|
1495
|
-
);
|
|
1557
|
+
return generateBFFCallFunctionWithSwaCredentials(true);
|
|
1496
1558
|
}
|
|
1497
1559
|
|
|
1498
1560
|
export function generateSwaAuthHelperTS(): string {
|
|
@@ -15,6 +15,27 @@ export interface UIAuthOptions {
|
|
|
15
15
|
authContextImport?: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* 認証付きCRUDルートを、ページ側のuseAuthと同じAuthProviderで囲むレイアウトを生成する。
|
|
20
|
+
*/
|
|
21
|
+
export function generateAuthLayout(model: ModelInfo, authOptions?: UIAuthOptions): string | undefined {
|
|
22
|
+
if (!authOptions) return undefined;
|
|
23
|
+
|
|
24
|
+
const authContextImport = authOptions.authContextImport ?? '@/lib/auth/auth-context';
|
|
25
|
+
|
|
26
|
+
return `import { AuthProvider } from '${authContextImport}';
|
|
27
|
+
import type { ReactNode } from 'react';
|
|
28
|
+
|
|
29
|
+
export default function ${model.name}Layout({
|
|
30
|
+
children,
|
|
31
|
+
}: {
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}) {
|
|
34
|
+
return <AuthProvider>{children}</AuthProvider>;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
/**
|
|
19
40
|
* 一覧画面を生成
|
|
20
41
|
*/
|