1
0
forked from baron/baron-sso

모바일 fallback 변경. .env유출 가능성 차단

This commit is contained in:
2026-05-26 11:30:00 +09:00
parent 0eb6dabdc1
commit e481ae2821
18 changed files with 177 additions and 52 deletions

View File

@@ -0,0 +1,97 @@
import { expect, test, type Page } from '@playwright/test';
type SigninCase = {
path: '/ko/signin' | '/en/signin';
theme: 'light' | 'dark';
};
const signinCases: SigninCase[] = [
{ path: '/ko/signin', theme: 'light' },
{ path: '/ko/signin', theme: 'dark' },
{ path: '/en/signin', theme: 'light' },
{ path: '/en/signin', theme: 'dark' },
];
async function mockPublicApis(page: Page): Promise<void> {
await page.route('**/api/v1/**', async (route) => {
const requestUrl = new URL(route.request().url());
if (requestUrl.pathname.endsWith('/api/v1/user/me')) {
await route.fulfill({
status: 401,
contentType: 'application/json',
body: JSON.stringify({ error: 'unauthorized' }),
});
return;
}
if (requestUrl.pathname.endsWith('/api/v1/auth/tenant-info')) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({}),
});
return;
}
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ ok: true }),
});
});
}
async function expectFlutterCanvasRendered(page: Page): Promise<void> {
const canvas = page.locator('canvas').first();
await expect(canvas).toBeVisible({ timeout: 30_000 });
const box = await canvas.boundingBox();
expect(box?.width ?? 0).toBeGreaterThan(100);
expect(box?.height ?? 0).toBeGreaterThan(100);
}
async function seedAuthTheme(page: Page, theme: SigninCase['theme']): Promise<void> {
await page.addInitScript((themeValue) => {
window.localStorage.setItem('userfront_auth_theme', themeValue);
window.localStorage.setItem('flutter.userfront_auth_theme', themeValue);
}, theme);
}
test.describe('UserFront signin runtime matrix', () => {
test.beforeEach(async ({ page }) => {
await mockPublicApis(page);
});
for (const entry of signinCases) {
test(`${entry.path} renders in ${entry.theme} theme`, async ({ page }) => {
await seedAuthTheme(page, entry.theme);
await page.goto(entry.path);
await expect(page).toHaveURL(new RegExp(`${entry.path}(?:\\?.*)?$`));
await expectFlutterCanvasRendered(page);
});
}
test('signin uses configured BACKEND_URL for public API requests', async ({
page,
}) => {
const expectedBackendOrigin = process.env.EXPECTED_BACKEND_ORIGIN;
test.skip(!expectedBackendOrigin, 'set EXPECTED_BACKEND_ORIGIN');
const requestedApiOrigins = new Set<string>();
page.on('request', (request) => {
const requestUrl = new URL(request.url());
if (requestUrl.pathname.startsWith('/api/v1/')) {
requestedApiOrigins.add(requestUrl.origin);
}
});
for (const entry of signinCases) {
await seedAuthTheme(page, entry.theme);
await page.goto(entry.path);
await expectFlutterCanvasRendered(page);
await expect
.poll(() => [...requestedApiOrigins], { timeout: 30_000 })
.toContain(expectedBackendOrigin);
expect(requestedApiOrigins).not.toContain('https://sso.example.test');
}
});
});

View File

@@ -1,6 +1,6 @@
import { expect, test, type BrowserContext, type Page } from '@playwright/test';
const USERFRONT_BASE_URL = process.env.USERFRONT_BASE_URL ?? 'https://sso-test.hmac.kr';
const USERFRONT_BASE_URL = process.env.USERFRONT_BASE_URL ?? 'https://sso.example.test';
const ADMINFRONT_URL = process.env.ADMINFRONT_URL ?? 'http://localhost:5173';
const LOGIN_ID = process.env.E2E_LOGIN_ID ?? '';
const PASSWORD = process.env.E2E_PASSWORD ?? '';
@@ -134,7 +134,7 @@ async function loginAdminFront(context: BrowserContext): Promise<Page> {
if (/\/login$/.test(page.url())) {
const authorizeUrl = await page.evaluate(() => {
const origin = window.location.origin;
const authority = 'https://sso-test.hmac.kr/oidc';
const authority = `${USERFRONT_BASE_URL}/oidc`;
const params = new URLSearchParams({
client_id: 'adminfront',
redirect_uri: `${origin}/auth/callback`,