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 {
final url = Uri.parse('$_baseUrl/api/v1/client-log');
try {

View File

@@ -21,6 +21,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _smsCodeController = TextEditingController();
bool _smsSent = false;
@override
void initState() {
@@ -74,6 +76,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_emailController.dispose();
_passwordController.dispose();
_phoneController.dispose();
_smsCodeController.dispose();
super.dispose();
}
@@ -233,66 +236,20 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (phone.isEmpty) return;
try {
// 1. Init via Proxy
final initData = await AuthProxyService.initEnchantedLink(phone);
final pendingRef = initData['pendingRef'];
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)");
}
}
}
await AuthProxyService.sendSms(phone);
setState(() {
_smsSent = true;
});
} catch (e) {
if (mounted && Navigator.canPop(context)) Navigator.of(context).pop();
_showError("SMS Enchanted Link Failed (Proxy): $e");
_showError("Failed to send SMS: $e");
}
}
Future<void> _handleSmsVerification() async {
// For now, just show a success message
_showSuccessDialog();
}
void _showError(String message) {
if (!mounted) return;
@@ -388,23 +345,42 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
// Phone/SMS Form
Column(
children: [
TextField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: "Phone Number",
hintText: "+82 10-1234-5678",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.phone_android),
if (!_smsSent) ...[
TextField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: "Phone Number",
hintText: "+82 10-1234-5678",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.phone_android),
),
),
),
const SizedBox(height: 24),
FilledButton(
onPressed: _handleSmsLogin,
style: FilledButton.styleFrom(
minimumSize: const Size.fromHeight(50),
const SizedBox(height: 24),
FilledButton(
onPressed: _handleSmsLogin,
style: FilledButton.styleFrom(
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"),
),
],
],
),
],