From 920c98a8f833a665a770d98b95bb16b41915bce5 Mon Sep 17 00:00:00 2001 From: kyy Date: Tue, 27 Jan 2026 15:11:55 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20Descope?= =?UTF-8?q?=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=A0=95=EC=B1=85=20?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/presentation/signup_screen.dart | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/frontend/lib/features/auth/presentation/signup_screen.dart b/frontend/lib/features/auth/presentation/signup_screen.dart index bf705a41..82cfb6b7 100644 --- a/frontend/lib/features/auth/presentation/signup_screen.dart +++ b/frontend/lib/features/auth/presentation/signup_screen.dart @@ -35,6 +35,8 @@ class _SignupScreenState extends State { bool _termsAccepted = false; bool _privacyAccepted = false; bool _isLoading = false; + Map? _policy; + bool _isPolicyLoading = false; // Inline Errors String? _emailError; @@ -58,6 +60,24 @@ class _SignupScreenState extends State { 'baroncs.co.kr': 'BARON', }; + @override + void initState() { + super.initState(); + _loadPolicy(); + } + + Future _loadPolicy() async { + setState(() => _isPolicyLoading = true); + try { + final policy = await AuthProxyService.fetchPasswordPolicy(); + if (mounted) setState(() => _policy = policy); + } catch (_) { + // Ignore errors, will use defaults + } finally { + if (mounted) setState(() => _isPolicyLoading = false); + } + } + @override void dispose() { _emailTimer?.cancel(); @@ -757,13 +777,40 @@ class _SignupScreenState extends State { ); } + String _buildPolicyDescription() { + if (_isPolicyLoading) { + return "비밀번호 정책을 불러오는 중입니다..."; + } + final minLength = (_policy?['minLength'] as int?) ?? 8; + final requiresLower = _policy?['lowercase'] ?? true; + final requiresUpper = _policy?['uppercase'] ?? true; + final requiresNumber = _policy?['number'] ?? true; + final requiresSymbol = _policy?['nonAlphanumeric'] ?? true; + + final parts = ["최소 $minLength자 이상"]; + if (requiresUpper) parts.add("대문자"); + if (requiresLower) parts.add("소문자"); + if (requiresNumber) parts.add("숫자"); + if (requiresSymbol) parts.add("특수문자"); + + return "보안 정책: ${parts.join(', ')}를 각각 최소 1자 이상 포함해야 합니다."; + } + Widget _buildStepPassword() { String p = _passwordController.text; - bool hasUpper = p.contains(RegExp(r'[A-Z]')); - bool hasLower = p.contains(RegExp(r'[a-z]')); - bool hasDigit = p.contains(RegExp(r'[0-9]')); - bool hasSpecial = p.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); - bool hasLength = p.length >= 12; + + // Default Policy Fallback + final minLength = (_policy?['minLength'] as int?) ?? 8; + final requiresLower = _policy?['lowercase'] ?? true; + final requiresUpper = _policy?['uppercase'] ?? true; + final requiresNumber = _policy?['number'] ?? true; + final requiresSymbol = _policy?['nonAlphanumeric'] ?? true; + + bool hasLength = p.length >= minLength; + bool hasUpper = !requiresUpper || p.contains(RegExp(r'[A-Z]')); + bool hasLower = !requiresLower || p.contains(RegExp(r'[a-z]')); + bool hasDigit = !requiresNumber || p.contains(RegExp(r'[0-9]')); + bool hasSpecial = !requiresSymbol || p.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -780,7 +827,7 @@ class _SignupScreenState extends State { const SizedBox(width: 10), Expanded( child: Text( - '보안 정책: 12자 이상, 대문자/소문자/숫자/특수문자를 각각 최소 1자 이상 포함해야 합니다.', + _buildPolicyDescription(), style: TextStyle(fontSize: 12, color: Colors.blue[800], fontWeight: FontWeight.w500), ), ), @@ -802,11 +849,11 @@ class _SignupScreenState extends State { Wrap( spacing: 10, children: [ - _cryptoCheck('12자 이상', hasLength), - _cryptoCheck('대문자', hasUpper), - _cryptoCheck('소문자', hasLower), - _cryptoCheck('숫자', hasDigit), - _cryptoCheck('특수문자', hasSpecial), + _cryptoCheck('$minLength자 이상', hasLength), + if (requiresUpper) _cryptoCheck('대문자', hasUpper), + if (requiresLower) _cryptoCheck('소문자', hasLower), + if (requiresNumber) _cryptoCheck('숫자', hasDigit), + if (requiresSymbol) _cryptoCheck('특수문자', hasSpecial), ], ), const SizedBox(height: 16),