forked from baron/baron-sso
비로그인 상태 OIDC 연동 시 발생하는 인증 루프 수정
This commit is contained in:
81
userfront-e2e/tests/oidc-login-challenge.spec.ts
Normal file
81
userfront-e2e/tests/oidc-login-challenge.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { expect, test, type Page, type Route } from '@playwright/test';
|
||||||
|
|
||||||
|
async function mockUserfrontApisForRepro(
|
||||||
|
page: Page,
|
||||||
|
options: { sessionStatus: number } = { sessionStatus: 401 },
|
||||||
|
): Promise<void> {
|
||||||
|
await page.route('**/api/v1/**', async (route: Route) => {
|
||||||
|
const requestUrl = new URL(route.request().url());
|
||||||
|
const path = requestUrl.pathname;
|
||||||
|
|
||||||
|
if (path.endsWith('/api/v1/user/me')) {
|
||||||
|
await route.fulfill({
|
||||||
|
status: options.sessionStatus,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ error: 'unauthorized' }),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith('/api/v1/client-log')) {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ ok: true }),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default mock for other APIs
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('Issue #345 Reproduction (Log-based Validation)', () => {
|
||||||
|
test('비로그인 상태에서 login_challenge와 함께 signin 진입 시 루프 없이 로그가 정상 출력되어야 한다', async ({ page }) => {
|
||||||
|
const logs: string[] = [];
|
||||||
|
page.on('console', msg => {
|
||||||
|
const text = msg.text();
|
||||||
|
logs.push(text);
|
||||||
|
console.log(`[Browser] ${text}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const requests: string[] = [];
|
||||||
|
page.on('request', request => {
|
||||||
|
if (request.isNavigationRequest()) {
|
||||||
|
requests.push(request.url());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await mockUserfrontApisForRepro(page, { sessionStatus: 401 });
|
||||||
|
|
||||||
|
const targetUrl = '/ko/signin?login_challenge=repro_challenge_12345';
|
||||||
|
await page.goto(targetUrl);
|
||||||
|
|
||||||
|
// WASM 앱 로딩 및 로직 실행 대기
|
||||||
|
await page.waitForTimeout(7000);
|
||||||
|
|
||||||
|
const currentUrl = page.url();
|
||||||
|
const signinNavigations = requests.filter(url => url.includes('/signin'));
|
||||||
|
|
||||||
|
// [검증 1] URL 유지 확인
|
||||||
|
expect(currentUrl).toContain('login_challenge=repro_challenge_12345');
|
||||||
|
|
||||||
|
// [검증 2] 리다이렉트 루프 발생 여부 확인 (최초 진입 1회만 있어야 함)
|
||||||
|
expect(signinNavigations.length).toBeLessThanOrEqual(1);
|
||||||
|
|
||||||
|
// [검증 3] 핵심 로직 로그 확인 (성공의 결정적 증거)
|
||||||
|
// 이전에는 여기서 Exception이 발생했으나, 이제는 아래 로그가 찍혀야 함
|
||||||
|
const hasSuccessLog = logs.some(log =>
|
||||||
|
log.includes('[Auth] OIDC auto-accept: No active session (status: 401)')
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(hasSuccessLog).toBe(true);
|
||||||
|
|
||||||
|
console.log('✅ 루프가 해결되었으며, 로그 검증을 통해 정상 동작을 확인했습니다.');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -161,7 +161,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
final provider = pendingProvider ?? AuthTokenStore.getProvider() ?? 'ory';
|
final provider = pendingProvider ?? AuthTokenStore.getProvider() ?? 'ory';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AuthProxyService.checkCookieSession();
|
final status = await AuthProxyService.getSessionStatus(useCookie: true);
|
||||||
|
if (status != 200) {
|
||||||
|
debugPrint("[Auth] Cookie session check: No active session (status: $status)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!shouldPromoteCookieSession(
|
if (!shouldPromoteCookieSession(
|
||||||
currentToken: AuthTokenStore.getToken(),
|
currentToken: AuthTokenStore.getToken(),
|
||||||
loginChallenge: loginChallenge,
|
loginChallenge: loginChallenge,
|
||||||
@@ -242,11 +247,16 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AuthProxyService.checkCookieSession();
|
// 401 응답은 세션이 없는 정상적인 상태이므로 예외로 처리하지 않고 우아하게 중단합니다.
|
||||||
AuthTokenStore.setCookieMode(
|
final status = await AuthProxyService.getSessionStatus(useCookie: true);
|
||||||
provider: AuthTokenStore.getProvider() ?? 'ory',
|
if (status == 200) {
|
||||||
);
|
AuthTokenStore.setCookieMode(
|
||||||
await _acceptOidcLoginAndRedirect();
|
provider: AuthTokenStore.getProvider() ?? 'ory',
|
||||||
|
);
|
||||||
|
await _acceptOidcLoginAndRedirect();
|
||||||
|
} else {
|
||||||
|
debugPrint("[Auth] OIDC auto-accept: No active session (status: $status)");
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("[Auth] OIDC auto-accept cookie check failed: $e");
|
debugPrint("[Auth] OIDC auto-accept cookie check failed: $e");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user