post_enrollment_app/lib/pages/service/TokenService.dart
2026-05-28 18:30:32 +05:30

193 lines
6.1 KiB
Dart
Executable File

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/environment.dart';
import '../../customAppBar/toastHelper.dart';
import '../../logger.dart';
import '../../router/app_router.dart';
import 'SessionManager.dart';
import 'web_session_storage.dart';
class TokenService {
static const postTokenKey = 'post_token';
static const preTokenKey = 'pre_token';
static const _secureStorage = FlutterSecureStorage(
iOptions: IOSOptions(
accessibility: KeychainAccessibility.first_unlock_this_device,
),
);
/// Web: tokens live only in [sessionStorage] (tab-scoped). Mobile: secure storage.
static Future<void> saveTokens({String? postToken, String? preToken}) async {
if (kIsWeb) {
if (postToken != null) {
WebSessionStorage.write(postTokenKey, postToken);
}
if (preToken != null) {
WebSessionStorage.write(preTokenKey, preToken);
}
} else {
if (postToken != null) {
await _secureStorage.write(key: postTokenKey, value: postToken);
}
if (preToken != null) {
await _secureStorage.write(key: preTokenKey, value: preToken);
}
}
}
static Future<String?> getPostToken() async {
if (kIsWeb) {
return WebSessionStorage.read(postTokenKey);
}
return _secureStorage.read(key: postTokenKey);
}
static Future<String?> getPreToken() async {
if (kIsWeb) {
return WebSessionStorage.read(preTokenKey);
}
return _secureStorage.read(key: preTokenKey);
}
/// Clears auth tokens. On web, clears entire [sessionStorage] (logout / session end).
static Future<void> clearTokens() async {
if (kIsWeb) {
WebSessionStorage.clearAll();
} else {
await _secureStorage.delete(key: postTokenKey);
await _secureStorage.delete(key: preTokenKey);
}
}
static Future<bool> hasValidToken() async {
final post = await getPostToken();
final pre = await getPreToken();
return (post != null && post.isNotEmpty) ||
(pre != null && pre.isNotEmpty);
}
/// Post token → home; pre-only → enrollment ([empDetails]).
static Future<String> defaultRouteForStoredTokens() async {
final post = await getPostToken();
if (post != null && post.isNotEmpty) return '/home';
final pre = await getPreToken();
if (pre != null && pre.isNotEmpty) return '/empDetails';
return '/login';
}
/// Clears auth tokens that older web builds stored in SharedPreferences (localStorage).
/// Call once at startup so they cannot be mistaken for a valid session.
static Future<void> clearLegacyWebAuthFromSharedPreferences() async {
if (!kIsWeb) return;
final p = await SharedPreferences.getInstance();
await p.remove(postTokenKey);
await p.remove(preTokenKey);
}
Future<String?> checkLoginPin() async {
try {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final empMobileNo = prefs.getString('empMobileNo');
final empEmailid = prefs.getString('empEmailid');
var params = <String, dynamic>{};
if (empMobileNo != null && empMobileNo.isNotEmpty) {
params = {'mobile_number': empMobileNo};
} else if (empEmailid != null && empEmailid.isNotEmpty) {
params = {'email_id': empEmailid};
}
final response = await http.post(
Uri.parse(Environment.apiUrlEnrollment + 'checkMpin'),
body: json.encode(params),
headers: {
'Content-Type': 'application/json',
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
},
);
if (response.statusCode == 200) {
Map<String, dynamic> data = json.decode(response.body);
if (data['status'] == 'success') {
if (data['data'] != null) {
prefs.setString('mpinText', data['data']);
}
if (data['Mpin'] != null) {
prefs.setString('mpin', data['Mpin']);
}
if (data['is_biometric_enabled'] != null) {
prefs.setString(
'is_biometric_enabled', data['is_biometric_enabled']);
}
if (data['is_mpin_skipped'] != null) {
prefs.setString('is_mpin_skipped', data['is_mpin_skipped']);
return data['is_mpin_skipped'].toString();
}
}
} else {
logDebug('checkLoginPin API failed: ${response.statusCode}');
return null;
}
} catch (e) {
logDebug('checkLoginPin error: $e');
return null;
}
return null;
}
void _goAfterLogout(BuildContext context, String location) {
if (context.mounted && GoRouter.maybeOf(context) != null) {
context.go(location);
} else {
navigateAppTo(location);
}
}
Future<void> logout(BuildContext context) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String? mobileNumber = prefs.getString('empMobileNo');
String? mailID = prefs.getString('empEmailid');
final postToken = await getPostToken();
final hasPostToken = postToken != null && postToken.isNotEmpty;
String? isMpinSkipped;
if (!kIsWeb && hasPostToken) {
isMpinSkipped = await checkLoginPin();
}
logDebug('isMpinSkipped $isMpinSkipped hasPostToken $hasPostToken');
await SessionManager().clear();
if (kIsWeb) {
await prefs.clear();
_goAfterLogout(context, '/login');
} else {
if (mobileNumber != null) {
await prefs.setString('empMobileNo', mobileNumber);
}
if (mailID != null) {
await prefs.setString('empEmailid', mailID);
}
if (isMpinSkipped != null) {
await prefs.setString('is_mpin_skipped', isMpinSkipped);
}
if (hasPostToken && isMpinSkipped != null && isMpinSkipped == '0') {
_goAfterLogout(context, '/pinPage');
} else {
_goAfterLogout(context, '/login');
}
}
}
}