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 {
|
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 {
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user