1
0
forked from baron/baron-sso
Files
baron-sso/userfront/lib/features/auth/domain/login_challenge_resolver.dart

196 lines
5.4 KiB
Dart

enum LoginChallengeSource { widget, uriQuery, rawSearch, rawHref, missing }
class LoginChallengeResolution {
final String? value;
final LoginChallengeSource source;
final bool uriHasLoginChallenge;
final bool rawSearchHasLoginChallenge;
final bool rawHrefHasLoginChallenge;
const LoginChallengeResolution({
required this.value,
required this.source,
required this.uriHasLoginChallenge,
required this.rawSearchHasLoginChallenge,
required this.rawHrefHasLoginChallenge,
});
Map<String, Object?> toDiagnostics() {
return {
'resolved_value_len': value?.length ?? 0,
'resolved_source': source.name,
'uri_has_login_challenge': uriHasLoginChallenge,
'raw_search_has_login_challenge': rawSearchHasLoginChallenge,
'raw_href_has_login_challenge': rawHrefHasLoginChallenge,
};
}
}
LoginChallengeResolution resolveLoginChallenge({
String? widgetLoginChallenge,
required Uri uri,
String? rawSearch,
String? rawHref,
}) {
final widgetValue = _normalizeChallenge(widgetLoginChallenge);
if (widgetValue != null) {
return const LoginChallengeResolution(
value: null,
source: LoginChallengeSource.widget,
uriHasLoginChallenge: false,
rawSearchHasLoginChallenge: false,
rawHrefHasLoginChallenge: false,
).copyWith(value: widgetValue);
}
final uriValue = _normalizeChallenge(uri.queryParameters['login_challenge']);
if (uriValue != null) {
return const LoginChallengeResolution(
value: null,
source: LoginChallengeSource.uriQuery,
uriHasLoginChallenge: true,
rawSearchHasLoginChallenge: false,
rawHrefHasLoginChallenge: false,
).copyWith(value: uriValue);
}
final rawSearchValue = _normalizeChallenge(
_extractQueryParamFromRawQuery(rawSearch, 'login_challenge'),
);
if (rawSearchValue != null) {
return const LoginChallengeResolution(
value: null,
source: LoginChallengeSource.rawSearch,
uriHasLoginChallenge: false,
rawSearchHasLoginChallenge: true,
rawHrefHasLoginChallenge: false,
).copyWith(value: rawSearchValue);
}
final rawHrefValue = _normalizeChallenge(
_extractQueryParamFromRawHref(rawHref, 'login_challenge'),
);
if (rawHrefValue != null) {
return const LoginChallengeResolution(
value: null,
source: LoginChallengeSource.rawHref,
uriHasLoginChallenge: false,
rawSearchHasLoginChallenge: false,
rawHrefHasLoginChallenge: true,
).copyWith(value: rawHrefValue);
}
return const LoginChallengeResolution(
value: null,
source: LoginChallengeSource.missing,
uriHasLoginChallenge: false,
rawSearchHasLoginChallenge: false,
rawHrefHasLoginChallenge: false,
);
}
String? _normalizeChallenge(String? value) {
final trimmed = value?.trim();
if (trimmed == null || trimmed.isEmpty) {
return null;
}
return trimmed;
}
String? _extractQueryParamFromRawHref(String? rawHref, String key) {
final href = rawHref?.trim();
if (href == null || href.isEmpty) {
return null;
}
final parsed = Uri.tryParse(href);
final fromParsed = parsed?.queryParameters[key];
final normalizedParsed = _normalizeChallenge(fromParsed);
if (normalizedParsed != null) {
return normalizedParsed;
}
final question = href.indexOf('?');
if (question < 0) {
return null;
}
final hash = href.indexOf('#', question + 1);
final rawQuery = hash < 0
? href.substring(question + 1)
: href.substring(question + 1, hash);
return _extractQueryParamFromRawQuery(rawQuery, key);
}
String? _extractQueryParamFromRawQuery(String? rawQuery, String key) {
final query = rawQuery?.trim();
if (query == null || query.isEmpty) {
return null;
}
final normalizedQuery = query.startsWith('?') ? query.substring(1) : query;
if (normalizedQuery.isEmpty) {
return null;
}
try {
final parsed = Uri.splitQueryString(normalizedQuery);
final value = _normalizeChallenge(parsed[key]);
if (value != null) {
return value;
}
} catch (_) {
// URI 파싱이 실패하면 수동 파싱으로 보완합니다.
}
for (final pair in normalizedQuery.split('&')) {
if (pair.isEmpty) {
continue;
}
final equalIndex = pair.indexOf('=');
final rawKey = equalIndex < 0 ? pair : pair.substring(0, equalIndex);
final decodedKey = _decodeQueryComponentSafe(rawKey);
if (decodedKey != key) {
continue;
}
if (equalIndex < 0) {
return null;
}
final rawValue = pair.substring(equalIndex + 1);
final decodedValue = _normalizeChallenge(
_decodeQueryComponentSafe(rawValue),
);
if (decodedValue != null) {
return decodedValue;
}
}
return null;
}
String _decodeQueryComponentSafe(String value) {
try {
return Uri.decodeQueryComponent(value);
} catch (_) {
return value;
}
}
extension on LoginChallengeResolution {
LoginChallengeResolution copyWith({
String? value,
LoginChallengeSource? source,
bool? uriHasLoginChallenge,
bool? rawSearchHasLoginChallenge,
bool? rawHrefHasLoginChallenge,
}) {
return LoginChallengeResolution(
value: value ?? this.value,
source: source ?? this.source,
uriHasLoginChallenge: uriHasLoginChallenge ?? this.uriHasLoginChallenge,
rawSearchHasLoginChallenge:
rawSearchHasLoginChallenge ?? this.rawSearchHasLoginChallenge,
rawHrefHasLoginChallenge:
rawHrefHasLoginChallenge ?? this.rawHrefHasLoginChallenge,
);
}
}