forked from baron/baron-sso
SMS 인증 코드 발송 UI 및 백엔드 API 연동
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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"),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user