1
0
forked from baron/baron-sso

SMS 인증 코드 발송 UI 및 백엔드 API 연동

This commit is contained in:
2026-01-06 15:01:02 +09:00
parent fec4a1cd55
commit cd0ca7a421
2 changed files with 63 additions and 71 deletions

View File

@@ -58,6 +58,22 @@ class AuthProxyService {
} }
} }
static Future<void> sendSms(String phoneNumber) async {
final url = Uri.parse('$_baseUrl/api/v1/auth/sms');
final response = await http.post(
url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'phoneNumber': phoneNumber,
}),
);
if (response.statusCode != 200) {
throw Exception('Failed to send SMS: ${response.body}');
}
}
static Future<void> logError(String message) async { static Future<void> logError(String message) async {
final url = Uri.parse('$_baseUrl/api/v1/client-log'); final url = Uri.parse('$_baseUrl/api/v1/client-log');
try { try {

View File

@@ -21,6 +21,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final TextEditingController _emailController = TextEditingController(); final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController(); final TextEditingController _passwordController = TextEditingController();
final TextEditingController _phoneController = TextEditingController(); final TextEditingController _phoneController = TextEditingController();
final TextEditingController _smsCodeController = TextEditingController();
bool _smsSent = false;
@override @override
void initState() { void initState() {
@@ -74,6 +76,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_emailController.dispose(); _emailController.dispose();
_passwordController.dispose(); _passwordController.dispose();
_phoneController.dispose(); _phoneController.dispose();
_smsCodeController.dispose();
super.dispose(); super.dispose();
} }
@@ -233,66 +236,20 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (phone.isEmpty) return; if (phone.isEmpty) return;
try { try {
// 1. Init via Proxy await AuthProxyService.sendSms(phone);
final initData = await AuthProxyService.initEnchantedLink(phone); setState(() {
final pendingRef = initData['pendingRef']; _smsSent = true;
});
if (mounted) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: const Text("Check your Messages"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("We sent a message to $phone"),
const SizedBox(height: 16),
const LinearProgressIndicator(),
const SizedBox(height: 16),
],
),
),
);
// 2. Poll via Proxy
final pollData = await AuthProxyService.pollEnchantedLink(pendingRef);
String sessionToken = "";
if (pollData['sessionToken'] is Map) {
sessionToken = pollData['sessionToken']['jwt'] ?? "";
} else if (pollData['sessionToken'] is String) {
sessionToken = pollData['sessionToken'];
}
if (sessionToken.isEmpty) {
throw Exception("Invalid session token received");
}
await AuditService.logEvent(
userId: phone,
eventType: 'login_success',
status: 'success',
details: 'Method: SMS/EnchantedLink/Proxy',
);
if (mounted) {
Navigator.of(context).pop(); // Close Dialog
if (WebAuthIntegration.isPopup()) {
WebAuthIntegration.sendLoginSuccess(sessionToken);
_showError("Login Successful! You can close this window.");
} else {
_showError("Login Successful (Standalone)");
}
}
}
} catch (e) { } catch (e) {
if (mounted && Navigator.canPop(context)) Navigator.of(context).pop(); _showError("Failed to send SMS: $e");
_showError("SMS Enchanted Link Failed (Proxy): $e");
} }
} }
Future<void> _handleSmsVerification() async {
// For now, just show a success message
_showSuccessDialog();
}
void _showError(String message) { void _showError(String message) {
if (!mounted) return; if (!mounted) return;
@@ -388,23 +345,42 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
// Phone/SMS Form // Phone/SMS Form
Column( Column(
children: [ children: [
TextField( if (!_smsSent) ...[
controller: _phoneController, TextField(
decoration: const InputDecoration( controller: _phoneController,
labelText: "Phone Number", decoration: const InputDecoration(
hintText: "+82 10-1234-5678", labelText: "Phone Number",
border: OutlineInputBorder(), hintText: "+82 10-1234-5678",
prefixIcon: Icon(Icons.phone_android), border: OutlineInputBorder(),
prefixIcon: Icon(Icons.phone_android),
),
), ),
), const SizedBox(height: 24),
const SizedBox(height: 24), FilledButton(
FilledButton( onPressed: _handleSmsLogin,
onPressed: _handleSmsLogin, style: FilledButton.styleFrom(
style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(50),
minimumSize: const Size.fromHeight(50), ),
child: const Text("Send Verification Code"),
), ),
child: const Text("Send Login Link"), ] else ...[
), TextField(
controller: _smsCodeController,
decoration: const InputDecoration(
labelText: "Verification Code",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.password),
),
),
const SizedBox(height: 24),
FilledButton(
onPressed: _handleSmsVerification,
style: FilledButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
child: const Text("Verify Code"),
),
],
], ],
), ),
], ],