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

@@ -1,9 +1,9 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
const _compileTimeEnv = {
'APP_ENV': String.fromEnvironment('APP_ENV'),
'BACKEND_URL': String.fromEnvironment('BACKEND_URL'),
'CLIENT_LOG_DEBUG': String.fromEnvironment('CLIENT_LOG_DEBUG'),
'USERFRONT_DEBUG_LOG': String.fromEnvironment('USERFRONT_DEBUG_LOG'),
'USERFRONT_URL': String.fromEnvironment('USERFRONT_URL'),
};
String runtimeOriginFallback() {
@@ -13,17 +13,10 @@ String runtimeOriginFallback() {
return origin;
}
} catch (_) {}
return 'https://sso-test.hmac.kr';
return '';
}
String envOrDefault(String key, String fallback) {
if (dotenv.isInitialized) {
final value = dotenv.env[key];
if (value != null && value.trim().isNotEmpty) {
return value;
}
}
final compileTimeValue = _compileTimeEnv[key];
if (compileTimeValue != null && compileTimeValue.trim().isNotEmpty) {
return compileTimeValue;

View File

@@ -150,14 +150,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_dotenv:
dependency: "direct main"
description:
name: flutter_dotenv
sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_driver:
dependency: transitive
description: flutter
@@ -276,14 +268,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
leak_tracker:
dependency: transitive
description:

View File

@@ -39,7 +39,6 @@ dependencies:
flutter_riverpod: ^3.0.3
go_router: ^17.0.1
http: ^1.6.0
flutter_dotenv: ^6.0.0
flutter_svg: ^2.2.1
url_launcher: ^6.3.2
logging: ^1.2.0

View File

@@ -10,8 +10,10 @@ set -- flutter run \
--web-hostname 0.0.0.0 \
--web-port "${USERFRONT_INTERNAL_PORT:-5000}" \
--wasm \
--dart-define=BACKEND_URL="${BACKEND_URL:-}" \
--dart-define=CLIENT_LOG_DEBUG="${CLIENT_LOG_DEBUG:-false}" \
--dart-define=APP_ENV="${APP_ENV:-dev}" \
--dart-define=USERFRONT_URL="${USERFRONT_URL:-}" \
${USERFRONT_FLUTTER_RUN_FLAGS:-} \
--no-web-resources-cdn

View File

@@ -8,7 +8,7 @@ void main() {
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',
rawHref: 'https://sso.example.test/ko/login?login_challenge=raw-href',
);
expect(resolved.value, 'widget-challenge');
@@ -46,7 +46,7 @@ void main() {
uri: Uri.parse('/ko/login'),
rawSearch: '',
rawHref:
'https://sso-test.hmac.kr/ko/login?a=1&login_challenge=raw-href-value#fragment',
'https://sso.example.test/ko/login?a=1&login_challenge=raw-href-value#fragment',
);
expect(resolved.value, 'raw-href-value');
@@ -59,7 +59,7 @@ void main() {
widgetLoginChallenge: null,
uri: Uri.parse('/ko/login'),
rawSearch: '',
rawHref: 'https://sso-test.hmac.kr/ko/login?x=1',
rawHref: 'https://sso.example.test/ko/login?x=1',
);
expect(resolved.value, isNull);

View File

@@ -5,12 +5,12 @@ void main() {
group('oidc_redirect_guard', () {
test('http/https 절대 URL만 허용', () {
final ok = validateOidcRedirectTarget(
'https://sso-test.hmac.kr/oidc/oauth2/auth?client_id=devfront&login_verifier=abc&state=xyz&code_challenge=ccc&code_challenge_method=S256&response_type=code&scope=openid%20profile&redirect_uri=http%3A%2F%2Flocalhost%3A5174%2Fcallback',
'https://sso.example.test/oidc/oauth2/auth?client_id=devfront&login_verifier=abc&state=xyz&code_challenge=ccc&code_challenge_method=S256&response_type=code&scope=openid%20profile&redirect_uri=http%3A%2F%2Flocalhost%3A5174%2Fcallback',
);
expect(ok.isValid, isTrue);
expect(ok.reason, 'ok');
expect(ok.scheme, 'https');
expect(ok.host, 'sso-test.hmac.kr');
expect(ok.host, 'sso.example.test');
expect(ok.path, '/oidc/oauth2/auth');
expect(ok.isOidcAuthPath, isTrue);
expect(ok.queryParamCount, 8);

View File

@@ -7,7 +7,7 @@ void main() {
final action = decidePasswordLoginNextAction(
hasLoginChallenge: true,
redirectTo:
'https://sso-test.hmac.kr/oidc/oauth2/auth?login_verifier=a',
'https://sso.example.test/oidc/oauth2/auth?login_verifier=a',
jwt: 'jwt-token',
);

View File

@@ -0,0 +1,43 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:userfront/core/services/runtime_env.dart';
const _expectedBackendUrl = String.fromEnvironment('BACKEND_URL');
const _expectedUserfrontUrl = String.fromEnvironment('USERFRONT_URL');
void main() {
group('runtime env compile-time defines', () {
test('runtime fallback is empty outside a browser origin', () {
expect(runtimeOriginFallback(), isEmpty);
});
test('BACKEND_URL dart-define overrides runtime origin fallback when set', () {
if (_expectedBackendUrl.isEmpty) {
expect(runtimeBackendUrl(), runtimeOriginFallback());
return;
}
expect(runtimeBackendUrl(), sanitizedUrl(_expectedBackendUrl));
});
test(
'USERFRONT_URL dart-define overrides runtime origin fallback when set',
() {
if (_expectedUserfrontUrl.isEmpty) {
expect(runtimeUserfrontUrl(), runtimeOriginFallback());
return;
}
expect(runtimeUserfrontUrl(), sanitizedUrl(_expectedUserfrontUrl));
},
);
test('dart-define URLs are sanitized', () {
if (_expectedBackendUrl.isEmpty || _expectedUserfrontUrl.isEmpty) {
return;
}
expect(runtimeBackendUrl(), isNot(endsWith('/')));
expect(runtimeUserfrontUrl(), isNot(endsWith('/')));
});
});
}