Created
January 19, 2022 06:44
-
-
Save dominicmh/8888cfc2e078bf5196d29b0c6140eae0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class IosAppVerification implements MfaAppVerification { | |
final _client = GipClient(); | |
@override | |
Future<StartMfaPhoneRequestInfo> createMfaRequestInfo( | |
String phoneNumber) async { | |
final futureAppVerificationInfo = _interceptAppVerificationNotification(); | |
final apnsToken = await _getApnsToken(); | |
final response = await _client.verifyIosClient(apnsToken); | |
final notificationTimeLimit = Duration( | |
seconds: int.tryParse(response.json?['suggestedTimeout']) ?? 5); | |
final appVerificationInfo = await futureAppVerificationInfo.timeout( | |
notificationTimeLimit, | |
onTimeout: () => throw TimeoutException( | |
"The expected app verification notification wasn't received within time limit. Possibly, due to disabled background refresh in the user's device settings. Would require reCAPTCHA verification for MFA."), | |
); | |
return StartMfaPhoneRequestInfo.ios( | |
phoneNumber: phoneNumber, | |
receipt: appVerificationInfo.receipt, | |
secret: appVerificationInfo.secret, | |
); | |
} | |
Future<String> _getApnsToken() async { | |
final apnsToken = await FirebaseMessaging.instance.getAPNSToken(); | |
if (apnsToken == null) { | |
throw Exception( | |
"APNS Token was null. Possibly, it was unintendedly requested on Android."); | |
} else { | |
return apnsToken; | |
} | |
} | |
Future<_AppVerificationInfo> _interceptAppVerificationNotification() async => | |
await FirebaseMessaging.onMessage | |
.map((message) => _extractAppVerificationInfo(message)) | |
.where((appVerificationInfo) => appVerificationInfo != null) | |
.cast<_AppVerificationInfo>() // requires prior null check | |
.first; | |
static _AppVerificationInfo? _extractAppVerificationInfo( | |
RemoteMessage message) { | |
print("Parsing push notification: ${message.data}"); | |
try { | |
final verificationInfoData = | |
json.decode(message.data['com.google.firebase.auth']); | |
return _AppVerificationInfo( | |
receipt: verificationInfoData['receipt']!, | |
secret: verificationInfoData['secret']!, | |
); | |
} catch (e) { | |
print( | |
"Received notification wasn't intended for app verification and is ignored. ${e.toString()}"); | |
return null; | |
} | |
} | |
} | |
class _AppVerificationInfo { | |
final String receipt; | |
final String secret; | |
_AppVerificationInfo({required this.receipt, required this.secret}); | |
} | |
class GipClient { | |
… | |
// ref. https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/verifyIosClient | |
Future<ApiResponse> verifyIosClient(String apnsToken) { | |
const method = 'accounts:verifyIosClient'; | |
final body = <String, dynamic>{ | |
'appToken': apnsToken, | |
'isSandbox': true, // TODO: Handle properly | |
}; | |
return _post( | |
method: method, | |
body: body, | |
pathPrefix: '/v1', | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment