1
0
forked from baron/baron-sso

OIDC로 빠지는 분기 점검 login_challenge 복구 fallback 추가

This commit is contained in:
Lectom C Han
2026-02-19 13:54:40 +09:00
parent f617467082
commit 33660cfdcf
7 changed files with 400 additions and 4 deletions

View File

@@ -0,0 +1,69 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:userfront/features/auth/domain/login_challenge_resolver.dart';
void main() {
group('login_challenge_resolver', () {
test('widget 값이 있으면 최우선으로 사용', () {
final resolved = resolveLoginChallenge(
widgetLoginChallenge: 'widget-challenge',
uri: Uri.parse('/ko/login'),
rawSearch: '?login_challenge=raw-search',
rawHref: 'https://sso-test.hmac.kr/ko/login?login_challenge=raw-href',
);
expect(resolved.value, 'widget-challenge');
expect(resolved.source, LoginChallengeSource.widget);
});
test('widget 값이 없으면 URI query에서 복구', () {
final resolved = resolveLoginChallenge(
widgetLoginChallenge: null,
uri: Uri.parse('/ko/login?login_challenge=uri-query'),
rawSearch: '',
rawHref: '',
);
expect(resolved.value, 'uri-query');
expect(resolved.source, LoginChallengeSource.uriQuery);
});
test('URI query가 비어 있으면 raw search에서 복구', () {
final resolved = resolveLoginChallenge(
widgetLoginChallenge: null,
uri: Uri.parse('/ko/login'),
rawSearch: '?login_challenge=raw-search-value&x=1',
rawHref: '',
);
expect(resolved.value, 'raw-search-value');
expect(resolved.source, LoginChallengeSource.rawSearch);
expect(resolved.rawSearchHasLoginChallenge, isTrue);
});
test('raw search도 비어 있으면 raw href에서 복구', () {
final resolved = resolveLoginChallenge(
widgetLoginChallenge: null,
uri: Uri.parse('/ko/login'),
rawSearch: '',
rawHref:
'https://sso-test.hmac.kr/ko/login?a=1&login_challenge=raw-href-value#fragment',
);
expect(resolved.value, 'raw-href-value');
expect(resolved.source, LoginChallengeSource.rawHref);
expect(resolved.rawHrefHasLoginChallenge, isTrue);
});
test('값이 전부 없으면 missing', () {
final resolved = resolveLoginChallenge(
widgetLoginChallenge: null,
uri: Uri.parse('/ko/login'),
rawSearch: '',
rawHref: 'https://sso-test.hmac.kr/ko/login?x=1',
);
expect(resolved.value, isNull);
expect(resolved.source, LoginChallengeSource.missing);
});
});
}

View File

@@ -28,6 +28,21 @@ Widget _buildTestApp(String initialLocation) {
);
},
),
GoRoute(
path: 'login',
builder: (context, state) {
final challenge = state.uri.queryParameters['login_challenge'];
final redirect =
state.uri.queryParameters['redirect_uri'] ??
state.uri.queryParameters['redirect_url'] ??
'';
return Scaffold(
body: Text(
'login|challenge=${challenge ?? ''}|redirect=$redirect',
),
);
},
),
GoRoute(
path: 'profile',
builder: (context, state) =>
@@ -45,7 +60,7 @@ Widget _buildTestApp(String initialLocation) {
final isLoggedIn =
AuthTokenStore.getToken() != null || AuthTokenStore.usesCookie();
final path = stripLocalePath(state.uri);
final isPublicPath = path == '/signin';
final isPublicPath = path == '/signin' || path == '/login';
if (isPublicPath) {
return null;
}
@@ -70,6 +85,25 @@ void main() {
LocaleRegistry.resetForTest();
});
testWidgets('/login: login_challenge와 redirect_uri를 전달', (tester) async {
final encodedRedirectUri = Uri.encodeComponent(
'https://rp.example.com/callback?x=1',
);
await tester.pumpWidget(
_buildTestApp(
'/en/login?login_challenge=lc_999&redirect_uri=$encodedRedirectUri',
),
);
await tester.pumpAndSettle();
expect(
find.text(
'login|challenge=lc_999|redirect=https://rp.example.com/callback?x=1',
),
findsOneWidget,
);
});
testWidgets('비로그인: redirect_uri/login_challenge가 signin으로 전달', (
tester,
) async {