diff --git a/lib/app/shared/constants/constants_json.dart b/lib/app/shared/constants/constants_json.dart index 18988261c..c67bb87d1 100644 --- a/lib/app/shared/constants/constants_json.dart +++ b/lib/app/shared/constants/constants_json.dart @@ -303,21 +303,18 @@ abstract class ConstantsJson { }; static const walletMetadataForIssuers = { + 'client_name': Parameters.walletName, + 'authorization_endpoint': Parameters.authorizationEndPoint, + 'response_types_supported': ['vp_token', 'id_token'], 'vp_formats_supported': { - 'jwt_vp': { - 'alg': ['ES256', 'ES256K', 'EdDSA'], - }, - 'jwt_vc': { - 'alg': ['ES256', 'ES256K', 'EdDSA'], + 'jwt_vc_json': { + 'alg_values_supported': ['ES256', 'ES256K', 'EdDSA'], }, 'jwt_vp_json': { - 'alg': ['ES256', 'ES256K', 'EdDSA'], - }, - 'jwt_vc_json': { - 'alg': ['ES256', 'ES256K', 'EdDSA'], + 'alg_values_supported': ['ES256', 'ES256K', 'EdDSA'], }, 'vc+sd-jwt': { - 'alg': ['ES256', 'ES256K', 'EdDSA'], + 'alg_values_supported': ['ES256', 'ES256K', 'EdDSA'], }, 'ldp_vp': { 'proof_type': [ @@ -336,23 +333,15 @@ abstract class ConstantsJson { ], }, }, - 'grant_types': ['authorization code', 'pre-authorized_code'], - 'redirect_uris': [Parameters.authorizationEndPoint], - 'subject_syntax_types_supported': ['did:key', 'did:jwk'], - 'subject_syntax_types_discriminations': [ - 'did:key:jwk_jcs-pub', - 'did:ebsi:v1', - ], - 'token_endpoint_auth_method_supported': [ - 'none', - 'client_id', - 'client_secret_post', - 'client_secret_basic', - 'client_secret_jwt', + 'client_id_schemes_supported': [ + 'did', + 'redirect_uri', + 'x509_san_dns', + 'verifier_attestation' ], - 'credential_offer_endpoint': ['openid-credential-offer://', 'haip://'], - 'client_name': '${Parameters.appName} wallet', - 'contacts': ['contact@talao.io'], + 'request_object_signing_alg_values_supported': ['ES256', 'ES256K', 'EdDSA'], + 'presentation_definition_uri_supported': true, + 'contacts': ['contact@talao.io'] }; static const walletMetadataForVerifiers = { diff --git a/lib/app/shared/constants/parameters.dart b/lib/app/shared/constants/parameters.dart index c292ce9e5..33ce7da44 100644 --- a/lib/app/shared/constants/parameters.dart +++ b/lib/app/shared/constants/parameters.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; class Parameters { static const int multipleCredentialsProcessDelay = 1; + // 'false' for talao + // 'true' for altme static const bool walletHandlesCrypto = true; static const AdvanceSettingsState defaultAdvanceSettingsState = @@ -70,6 +72,7 @@ class Parameters { chainChanged, accountsChanged, ]; + static const optionalEvents = ['message', 'disconnect', 'connect']; static const allEvents = [...requiredEvents, ...optionalEvents]; @@ -78,28 +81,42 @@ class Parameters { static const int maxEntries = 3; // 'Talao'for talao + // 'Altme' for altme static const String appName = 'Altme'; + // 'false' for talao + // 'true' for altme + static const bool importAndRestoreAtOnboarding = true; + // false for talao + // 'true' for altme static const bool supportDefiCompliance = true; + // false for talao + // 'true' for altme static const bool supportCryptoAccountOwnershipInDiscoverForEnterpriseMode = true; + // false for talao - static const bool showChainbornCard = false; - // false for talao - static const bool showTezotopiaCard = false; + // 'true' for altme + static const bool showChainbornCard = true; - //'https://app.talao.co/app/download/authorize' for Talao - static const String redirectUri = - 'https://app.altme.io/app/download/authorize'; + // false for talao + // true for altme + static const bool showTezotopiaCard = true; //'https://app.talao.co/app/download/callback' for Talao + // 'https://app.altme.io/app/download/callback' for altme static const String authorizationEndPoint = 'https://app.altme.io/app/download/callback'; - // 'talao_wallet'for talao - static const String walletName = 'altme_wallet'; + // 'Talao_wallet'for talao + // 'Altme_wallet' for altme + static const String walletName = 'Altme_wallet'; + + // 'https://app.talao.co/wallet_issuer'for talao + // 'https://app.altme.io/wallet_issuer' for altme + static const String walletIssuer = 'https://app.altme.io/wallet_issuer'; static const DidKeyType didKeyTypeForEbsiV3 = DidKeyType.ebsiv3; static const DidKeyType didKeyTypeForEbsiV4 = DidKeyType.ebsiv4; @@ -114,6 +131,7 @@ class Parameters { // static const Color seedColor = Color(0xff1EAADC); // ThemeMode.light for talao + // 'ThemeMode.dark' for altme static const ThemeMode defaultTheme = ThemeMode.dark; // Used to prevent display diff --git a/lib/app/shared/enum/status/app_status.dart b/lib/app/shared/enum/status/app_status.dart index 570065aa4..d94220fe8 100644 --- a/lib/app/shared/enum/status/app_status.dart +++ b/lib/app/shared/enum/status/app_status.dart @@ -9,5 +9,8 @@ enum AppStatus { idle, goBack, revoked, - walletProviderApproval, + addEnterpriseAccount, + updateEnterpriseAccount, + replaceEnterpriseAccount, + restoreWallet, } diff --git a/lib/app/shared/enum/type/profile/did_key_type.dart b/lib/app/shared/enum/type/profile/did_key_type.dart index 91caedd7b..0742dea83 100644 --- a/lib/app/shared/enum/type/profile/did_key_type.dart +++ b/lib/app/shared/enum/type/profile/did_key_type.dart @@ -39,6 +39,24 @@ extension DidKeyTypeX on DidKeyType { } } + String get didString { + switch (this) { + case DidKeyType.edDSA: + return 'did:key edDSA'; + case DidKeyType.secp256k1: + return 'did:key secp256k1'; + case DidKeyType.p256: + return 'did:key P-256'; + case DidKeyType.ebsiv3: + case DidKeyType.ebsiv4: + return 'did:key EBSI P-256'; + case DidKeyType.jwkP256: + return 'did:jwk P-256'; + case DidKeyType.jwtClientAttestation: + return ''; + } + } + String getTitle(AppLocalizations l10n) { switch (this) { case DidKeyType.edDSA: diff --git a/lib/app/shared/enum/type/profile/profile_type.dart b/lib/app/shared/enum/type/profile/profile_type.dart index 2921a27df..62b65164e 100644 --- a/lib/app/shared/enum/type/profile/profile_type.dart +++ b/lib/app/shared/enum/type/profile/profile_type.dart @@ -28,6 +28,8 @@ extension ProfileTypeX on ProfileType { } } + String get profileId => name; + bool get showSponseredBy { switch (this) { case ProfileType.custom: diff --git a/lib/app/shared/enum/type/restore_type.dart b/lib/app/shared/enum/type/restore_type.dart new file mode 100644 index 000000000..ac23db8eb --- /dev/null +++ b/lib/app/shared/enum/type/restore_type.dart @@ -0,0 +1 @@ +enum RestoreType { cryptoWallet, appBackup } diff --git a/lib/app/shared/enum/type/type.dart b/lib/app/shared/enum/type/type.dart index 481cf092d..a7d248c54 100644 --- a/lib/app/shared/enum/type/type.dart +++ b/lib/app/shared/enum/type/type.dart @@ -8,6 +8,7 @@ export 'message_type/message_type.dart'; export 'oidc4vc_type.dart'; export 'profile/did_key_type.dart'; export 'profile/profile.dart'; +export 'restore_type.dart'; export 'wallet_protection_type.dart'; export 'wallet_provider_type.dart'; export 'wallet_route_type.dart'; diff --git a/lib/app/shared/helper_functions/get_display.dart b/lib/app/shared/helper_functions/get_display.dart new file mode 100644 index 000000000..44600ad64 --- /dev/null +++ b/lib/app/shared/helper_functions/get_display.dart @@ -0,0 +1,33 @@ +dynamic getDisplay(dynamic value, String languageCode) { + if (value is! Map) return null; + + if (value.isEmpty) return null; + + if (value.containsKey('display')) { + final displays = value['display']; + if (displays is! List) return null; + if (displays.isEmpty) return null; + + final display = displays.firstWhere( + (element) => + element is Map && + element.containsKey('locale') && + element['locale'].toString().contains(languageCode), + orElse: () => displays.firstWhere( + (element) => + element is Map && + element.containsKey('locale') && + element['locale'].toString().contains('en'), + orElse: () => displays.firstWhere( + (element) => + element is Map && element.containsKey('locale'), + orElse: () => null, + ), + ), + ); + + return display; + } else { + return null; + } +} diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index 1232a445c..98a60a567 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -1417,7 +1417,7 @@ Future getFormattedStringOIDC4VPSIOPV2({ final data = ''' SCHEME : ${getSchemeFromUrl(url)}\n AUTHORIZATION REQUEST : -${response != null ? const JsonEncoder.withIndent(' ').convert(response) : 'None'}\n +${response != null ? const JsonEncoder.withIndent(' ').convert(response) : Uri.decodeComponent(url)}\n CLIENT METADATA : ${clientMetaData != null ? const JsonEncoder.withIndent(' ').convert(clientMetaData) : 'None'}\n PRESENTATION DEFINITION : @@ -1802,7 +1802,7 @@ List getStringCredentialsForToken({ } //(presentLdpVc, presentJwtVc, presentJwtVcJson, presentVcSdJwt) -(bool, bool, bool, bool) getPresentVCDetails({ +List getPresentVCDetails({ required VCFormatType vcFormatType, required PresentationDefinition presentationDefinition, required Map? clientMetaData, @@ -1811,154 +1811,70 @@ List getStringCredentialsForToken({ bool presentLdpVc = false; bool presentJwtVc = false; bool presentJwtVcJson = false; + bool presentJwtVcJsonLd = false; bool presentVcSdJwt = false; - if (vcFormatType == VCFormatType.auto) { - final credential = credentialsToBePresented.firstOrNull; + final supportingFormats = []; - if (credential == null) { - throw ResponseMessage( - data: { - 'error': 'invalid_request', - 'error_description': 'VC format is missing', - }, - ); - } + if (presentationDefinition.format != null) { + final format = presentationDefinition.format; + + /// ldp_vc + presentLdpVc = format?.ldpVc != null || format?.ldpVp != null; - final credentialFormat = credential.getFormat; + /// jwt_vc + presentJwtVc = format?.jwtVc != null || format?.jwtVp != null; - if (credentialFormat == VCFormatType.ldpVc.vcValue) { + /// jwt_vc_json + presentJwtVcJson = format?.jwtVcJson != null || format?.jwtVpJson != null; + + /// vc+sd-jwt + presentVcSdJwt = format?.vcSdJwt != null; + } else { + if (clientMetaData == null) { + /// credential manifest case presentLdpVc = true; - presentJwtVc = false; - presentJwtVcJson = false; - presentVcSdJwt = false; - } else if (credentialFormat == VCFormatType.jwtVc.vcValue) { - presentLdpVc = false; presentJwtVc = true; - presentJwtVcJson = false; - presentVcSdJwt = false; - } else if (credentialFormat == VCFormatType.jwtVcJson.vcValue) { - presentLdpVc = false; - presentJwtVc = false; presentJwtVcJson = true; - presentVcSdJwt = false; - } else if (credentialFormat == VCFormatType.vcSdJWT.vcValue) { - presentLdpVc = false; - presentJwtVc = false; - presentJwtVcJson = false; + presentJwtVcJsonLd = true; presentVcSdJwt = true; - } - } else { - final supportingFormats = []; - - if (presentationDefinition.format != null) { - final format = presentationDefinition.format; + } else { + final vpFormats = clientMetaData['vp_formats'] as Map; /// ldp_vc - presentLdpVc = format?.ldpVc != null || format?.ldpVp != null; + presentLdpVc = vpFormats.containsKey('ldp_vc'); /// jwt_vc - presentJwtVc = format?.jwtVc != null || format?.jwtVp != null; + presentJwtVc = vpFormats.containsKey('jwt_vc'); /// jwt_vc_json - presentJwtVcJson = format?.jwtVcJson != null || format?.jwtVpJson != null; - - /// vc+sd-jwt - presentVcSdJwt = format?.vcSdJwt != null; - } else { - if (clientMetaData == null) { - /// credential manifest case - if (vcFormatType == VCFormatType.ldpVc) { - presentLdpVc = true; - } else if (vcFormatType == VCFormatType.jwtVc) { - presentJwtVc = true; - } else if (vcFormatType == VCFormatType.jwtVcJson) { - presentJwtVcJson = true; - } else if (vcFormatType == VCFormatType.vcSdJWT) { - presentVcSdJwt = true; - } - } else { - final vpFormats = clientMetaData['vp_formats'] as Map; - - /// ldp_vc - presentLdpVc = vpFormats.containsKey('ldp_vc'); - - /// jwt_vc - presentJwtVc = vpFormats.containsKey('jwt_vc'); - - /// jwt_vc_json - presentJwtVcJson = vpFormats.containsKey('jwt_vc_json'); - - /// vc+sd-jwt - presentVcSdJwt = vpFormats.containsKey('vc+sd-jwt'); - } - if (!presentLdpVc && vcFormatType == VCFormatType.ldpVc) { - presentLdpVc = true; - } else if (!presentJwtVc && vcFormatType == VCFormatType.jwtVc) { - presentJwtVc = true; - } else if (!presentJwtVcJson && vcFormatType == VCFormatType.jwtVcJson) { - presentJwtVcJson = true; - } else if (!presentVcSdJwt && vcFormatType == VCFormatType.vcSdJWT) { - presentVcSdJwt = true; - } - } + presentJwtVcJson = vpFormats.containsKey('jwt_vc_json'); - if (!presentLdpVc && - !presentJwtVc && - !presentJwtVcJson && - !presentVcSdJwt) { - throw ResponseMessage( - data: { - 'error': 'invalid_request', - 'error_description': 'VC format is missing', - }, - ); - } - - /// create list of supported formats - if (presentLdpVc) supportingFormats.add(VCFormatType.ldpVc.vcValue); - if (presentJwtVc) supportingFormats.add(VCFormatType.jwtVc.vcValue); - if (presentJwtVcJson) supportingFormats.add(VCFormatType.jwtVcJson.vcValue); - if (presentVcSdJwt) supportingFormats.add(VCFormatType.vcSdJWT.vcValue); + /// jwt_vc_json-ld + presentJwtVcJson = vpFormats.containsKey('jwt_vc_json-ld'); - /// make sure only one of all are true - if (presentLdpVc && vcFormatType == VCFormatType.ldpVc) { - presentLdpVc = true; - presentJwtVc = false; - presentJwtVcJson = false; - presentVcSdJwt = false; - } else if (presentJwtVc && vcFormatType == VCFormatType.jwtVc) { - presentLdpVc = false; - presentJwtVc = true; - presentJwtVcJson = false; - presentVcSdJwt = false; - } else if (presentJwtVcJson && vcFormatType == VCFormatType.jwtVcJson) { - presentLdpVc = false; - presentJwtVc = false; - presentJwtVcJson = true; - presentVcSdJwt = false; - } else if (presentVcSdJwt && vcFormatType == VCFormatType.vcSdJWT) { - presentLdpVc = false; - presentJwtVc = false; - presentJwtVcJson = false; - presentVcSdJwt = true; + /// vc+sd-jwt + presentVcSdJwt = vpFormats.containsKey('vc+sd-jwt'); } + } - if ((presentLdpVc && vcFormatType != VCFormatType.ldpVc) || - (presentJwtVc && vcFormatType != VCFormatType.jwtVc) || - presentJwtVcJson && vcFormatType != VCFormatType.jwtVcJson || - presentVcSdJwt && vcFormatType != VCFormatType.vcSdJWT) { - throw ResponseMessage( - data: { - 'error': 'invalid_request', - 'error_description': 'Please switch to profile that supports format ' - '${supportingFormats.join('/')}.', - }, - ); - } + if (!presentLdpVc && !presentJwtVc && !presentJwtVcJson && !presentVcSdJwt) { + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'VC format is missing', + }, + ); } - return (presentLdpVc, presentJwtVc, presentJwtVcJson, presentVcSdJwt); + /// create list of supported formats + if (presentLdpVc) supportingFormats.add(VCFormatType.ldpVc); + if (presentJwtVc) supportingFormats.add(VCFormatType.jwtVc); + if (presentJwtVcJson) supportingFormats.add(VCFormatType.jwtVcJson); + if (presentJwtVcJsonLd) supportingFormats.add(VCFormatType.jwtVcJsonLd); + if (presentVcSdJwt) supportingFormats.add(VCFormatType.vcSdJWT); + + return supportingFormats; } List collectSdValues(Map data) { diff --git a/lib/app/shared/message_handler/response_message.dart b/lib/app/shared/message_handler/response_message.dart index 25cb0243f..fe07063d8 100644 --- a/lib/app/shared/message_handler/response_message.dart +++ b/lib/app/shared/message_handler/response_message.dart @@ -786,6 +786,7 @@ class ResponseMessage with MessageHandler { case ResponseString .RESPONSE_STRING_invalidPresentationDefinitionUriErrorDescription: return ResponseString + // ignore: lines_longer_than_80_chars .RESPONSE_STRING_invalidPresentationDefinitionUriErrorDescription .localise( context, diff --git a/lib/credentials/cubit/credentials_cubit.dart b/lib/credentials/cubit/credentials_cubit.dart index 99491fce4..1e28d4d37 100644 --- a/lib/credentials/cubit/credentials_cubit.dart +++ b/lib/credentials/cubit/credentials_cubit.dart @@ -495,11 +495,9 @@ class CredentialsCubit extends Cubit { Future recoverWallet({ required List credentials, - required bool isPolygonIdCredentials, }) async { - if (!isPolygonIdCredentials) { - await credentialsRepository.deleteAll(); - } + await credentialsRepository.deleteAll(); + for (final credential in credentials) { await credentialsRepository.insert(credential); } diff --git a/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart b/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart index bce92f810..07e289378 100644 --- a/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart +++ b/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart @@ -104,17 +104,23 @@ class HelpCenterView extends StatelessWidget { ), ], DrawerItem( - title: l10n.faqs, onTap: () { - Navigator.of(context).push(FAQsPage.route()); + LaunchUrl.launch('https://doc.wallet-provider.io/welcome'); }, + title: l10n.documentation, ), - DrawerItem( - onTap: () { - LaunchUrl.launch('https://${AltMeStrings.appContactWebsiteName}'); - }, - title: l10n.officialWebsite, - ), + // DrawerItem( + // title: l10n.faqs, + // onTap: () { + // Navigator.of(context).push(FAQsPage.route()); + // }, + // ), + // DrawerItem( + // onTap: () { + // LaunchUrl.launch('https://${AltMeStrings.appContactWebsiteName}'); + // }, + // title: l10n.officialWebsite, + // ), ], ), ); diff --git a/lib/dashboard/drawer/src/view/drawer_page.dart b/lib/dashboard/drawer/src/view/drawer_page.dart index cfb0dc05e..4d302eb08 100644 --- a/lib/dashboard/drawer/src/view/drawer_page.dart +++ b/lib/dashboard/drawer/src/view/drawer_page.dart @@ -170,15 +170,18 @@ class DrawerView extends StatelessWidget { }, ), const SizedBox(height: Sizes.spaceSmall), - DrawerCategoryItem( - title: l10n.activityLog, - subTitle: l10n.activityLogDescription, - onClick: () { - Navigator.of(context) - .push(ActivityLogPage.route()); - }, - ), - const SizedBox(height: Sizes.spaceSmall), + if (profileModel + .profileSetting.settingsMenu.displayActivityLog) ...[ + DrawerCategoryItem( + title: l10n.activityLog, + subTitle: l10n.activityLogDescription, + onClick: () { + Navigator.of(context) + .push(ActivityLogPage.route()); + }, + ), + const SizedBox(height: Sizes.spaceSmall), + ], DrawerCategoryItem( title: l10n.resetWallet, subTitle: l10n.resetWalletDescription, diff --git a/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart b/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart index 191e8ab98..c289d7157 100644 --- a/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart +++ b/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart @@ -55,6 +55,11 @@ class DidView extends StatelessWidget { return Container(); } + /// there is no new key for EBSI V4 + if (didKeyType == DidKeyType.ebsiv4) { + return Container(); + } + final title = didKeyType.getTitle(l10n); return DrawerItem( title: title, diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart index 1b453c4df..4125d8863 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart @@ -26,6 +26,12 @@ class DidKeyTypeWidget extends StatelessWidget { if (didKeyType == DidKeyType.jwtClientAttestation) { return Container(); } + + /// there is no new key for EBSI V4 + if (didKeyType == DidKeyType.ebsiv4) { + return Container(); + } + return Column( children: [ ListTile( @@ -66,7 +72,7 @@ class DidKeyTypeWidget extends StatelessWidget { ), ), title: Text( - didKeyType.formattedString, + didKeyType.didString, style: Theme.of(context).textTheme.bodyLarge, ), trailing: Icon( diff --git a/lib/dashboard/drawer/wallet_security/backup/backup.dart b/lib/dashboard/drawer/wallet_security/backup/backup.dart index 92084202c..78f28b7f0 100644 --- a/lib/dashboard/drawer/wallet_security/backup/backup.dart +++ b/lib/dashboard/drawer/wallet_security/backup/backup.dart @@ -1,4 +1,2 @@ export 'backup_credential/backup_credential.dart'; export 'backup_mnemonic/backup_mnemonic.dart'; -export 'backup_polygon_identity/backup_polygon_identity.dart'; -export 'src/src.dart'; diff --git a/lib/dashboard/drawer/wallet_security/backup/backup_credential/cubit/backup_credential_cubit.dart b/lib/dashboard/drawer/wallet_security/backup/backup_credential/cubit/backup_credential_cubit.dart index e5256d06c..121d65deb 100644 --- a/lib/dashboard/drawer/wallet_security/backup/backup_credential/cubit/backup_credential_cubit.dart +++ b/lib/dashboard/drawer/wallet_security/backup/backup_credential/cubit/backup_credential_cubit.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:altme/activity_log/activity_log.dart'; import 'package:altme/app/app.dart'; import 'package:altme/credentials/credentials.dart'; +import 'package:altme/dashboard/profile/cubit/profile_cubit.dart'; import 'package:altme/polygon_id/polygon_id.dart'; import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; @@ -24,6 +25,7 @@ class BackupCredentialCubit extends Cubit { required this.fileSaver, required this.polygonIdCubit, required this.activityLogManager, + required this.profileCubit, }) : super(const BackupCredentialState()); final SecureStorageProvider secureStorageProvider; @@ -32,12 +34,11 @@ class BackupCredentialCubit extends Cubit { final FileSaver fileSaver; final PolygonIdCubit polygonIdCubit; final ActivityLogManager activityLogManager; + final ProfileCubit profileCubit; Future encryptAndDownloadFile() async { emit(state.loading()); await Future.delayed(const Duration(milliseconds: 500)); - final mnemonic = - await secureStorageProvider.get(SecureStorageKeys.ssiMnemonic); final isPermissionStatusGranted = await getStoragePermission(); try { @@ -58,6 +59,38 @@ class BackupCredentialCubit extends Cubit { 'credentials': credentialModels, }; + final profileModel = profileCubit.state.model; + + message['profile'] = profileModel.profileType.profileId; + + if (profileModel.profileType == ProfileType.custom || + profileModel.profileType == ProfileType.enterprise) { + final profileSetting = profileModel.profileSetting.toJson(); + message['profileSetting'] = profileSetting; + } + + if (profileModel.profileType == ProfileType.enterprise) { + final email = await profileCubit.secureStorageProvider + .get(SecureStorageKeys.enterpriseEmail); + + final password = await profileCubit.secureStorageProvider + .get(SecureStorageKeys.enterprisePassword); + + final walletProvider = await profileCubit.secureStorageProvider + .get(SecureStorageKeys.enterpriseWalletProvider); + + final enterprise = { + 'email': email, + 'password': password, + 'walletProvider': walletProvider, + }; + + message['enterprise'] = enterprise; + } + + final mnemonic = + await secureStorageProvider.get(SecureStorageKeys.ssiMnemonic); + final encrypted = await cryptoKeys.encrypt(jsonEncode(message), mnemonic!); final encryptedString = jsonEncode(encrypted); diff --git a/lib/dashboard/drawer/wallet_security/backup/backup_credential/view/backup_credential_page.dart b/lib/dashboard/drawer/wallet_security/backup/backup_credential/view/backup_credential_page.dart index 97f0272d5..7ecc648a1 100644 --- a/lib/dashboard/drawer/wallet_security/backup/backup_credential/view/backup_credential_page.dart +++ b/lib/dashboard/drawer/wallet_security/backup/backup_credential/view/backup_credential_page.dart @@ -1,7 +1,7 @@ import 'package:altme/activity_log/activity_log.dart'; import 'package:altme/app/app.dart'; import 'package:altme/credentials/cubit/credentials_cubit.dart'; -import 'package:altme/dashboard/drawer/drawer.dart'; +import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/polygon_id/polygon_id.dart'; @@ -34,6 +34,7 @@ class BackupCredentialPage extends StatelessWidget { fileSaver: FileSaver.instance, polygonIdCubit: context.read(), activityLogManager: ActivityLogManager(getSecureStorage), + profileCubit: context.read(), ), child: const BackupCredentialView(), ); @@ -102,7 +103,9 @@ class BackupCredentialView extends StatelessWidget { ), const SizedBox(height: Sizes.spaceXLarge), Text( - l10n.saveBackupCredentialSubtitle, + Parameters.importAndRestoreAtOnboarding + ? l10n.saveBackupCredentialSubtitle + : l10n.saveBackupCredentialSubtitle2, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, ), diff --git a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/backup_polygon_identity.dart b/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/backup_polygon_identity.dart deleted file mode 100644 index 8c941dea3..000000000 --- a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/backup_polygon_identity.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'cubit/backup_polygon_identity_cubit.dart'; -export 'view/backup_polygon_identity_page.dart'; diff --git a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/cubit/backup_polygon_identity_cubit.dart b/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/cubit/backup_polygon_identity_cubit.dart deleted file mode 100644 index 15d4f256a..000000000 --- a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/cubit/backup_polygon_identity_cubit.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:altme/activity_log/activity_log.dart'; -import 'package:altme/app/app.dart'; -import 'package:altme/polygon_id/polygon_id.dart'; -import 'package:altme/wallet/cubit/wallet_cubit.dart'; - -import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; -import 'package:equatable/equatable.dart'; -import 'package:file_saver/file_saver.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:json_annotation/json_annotation.dart'; -import 'package:secure_storage/secure_storage.dart'; - -part 'backup_polygon_identity_cubit.g.dart'; -part 'backup_polygon_identity_state.dart'; - -class BackupPolygonIdIdentityCubit extends Cubit { - BackupPolygonIdIdentityCubit({ - required this.secureStorageProvider, - required this.cryptoKeys, - required this.walletCubit, - required this.fileSaver, - required this.polygonIdCubit, - required this.activityLogManager, - }) : super(const BackupPolygonIdIdentityState()); - - final SecureStorageProvider secureStorageProvider; - final CryptocurrencyKeys cryptoKeys; - final WalletCubit walletCubit; - final FileSaver fileSaver; - final PolygonIdCubit polygonIdCubit; - final ActivityLogManager activityLogManager; - - Future saveEncryptedFile() async { - emit(state.loading()); - await Future.delayed(const Duration(milliseconds: 500)); - final mnemonic = - await secureStorageProvider.get(SecureStorageKeys.ssiMnemonic); - final isPermissionStatusGranted = await getStoragePermission(); - - try { - if (!isPermissionStatusGranted) { - throw ResponseMessage( - message: ResponseString.STORAGE_PERMISSION_DENIED_MESSAGE, - ); - } - - final dateTime = getDateTimeWithoutSpace(); - final fileName = 'altme-polygonid-identity-$dateTime'; - - await polygonIdCubit.initialise(); - - String network = Parameters.POLYGON_MAIN_NETWORK; - - if (polygonIdCubit.state.currentNetwork == - PolygonIdNetwork.PolygonMainnet) { - network = Parameters.POLYGON_MAIN_NETWORK; - } else { - network = Parameters.POLYGON_TEST_NETWORK; - } - - final polygonCredentials = await polygonIdCubit.polygonId.backupIdentity( - mnemonic: mnemonic!, - network: network, - ); - - if (polygonCredentials == null) { - throw ResponseMessage( - message: ResponseString - .RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER, - ); - } - - final String encryptedString = polygonCredentials; - - final fileBytes = Uint8List.fromList(utf8.encode(encryptedString)); - - final filePath = await fileSaver.saveAs( - name: fileName, - bytes: fileBytes, - ext: 'txt', - mimeType: MimeType.text, - ); - - if (filePath != null && filePath.isEmpty) { - emit(state.copyWith(status: AppStatus.idle)); - } else { - await activityLogManager.saveLog(LogData(type: LogType.backupData)); - emit( - state.copyWith( - status: AppStatus.success, - filePath: filePath, - messageHandler: ResponseMessage( - message: ResponseString - .RESPONSE_STRING_BACKUP_CREDENTIAL_SUCCESS_MESSAGE, - ), - ), - ); - } - } catch (e) { - if (e is MessageHandler) { - emit(state.error(messageHandler: e)); - } else { - emit( - state.error( - messageHandler: ResponseMessage( - message: ResponseString.RESPONSE_STRING_BACKUP_CREDENTIAL_ERROR, - ), - ), - ); - } - } - } -} diff --git a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/cubit/backup_polygon_identity_state.dart b/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/cubit/backup_polygon_identity_state.dart deleted file mode 100644 index 6fd04c2fa..000000000 --- a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/cubit/backup_polygon_identity_state.dart +++ /dev/null @@ -1,53 +0,0 @@ -part of 'backup_polygon_identity_cubit.dart'; - -@JsonSerializable() -class BackupPolygonIdIdentityState extends Equatable { - const BackupPolygonIdIdentityState({ - this.status = AppStatus.init, - this.message, - this.filePath = '', - }); - - factory BackupPolygonIdIdentityState.fromJson(Map json) => - _$BackupPolygonIdIdentityStateFromJson(json); - - final AppStatus status; - final StateMessage? message; - final String filePath; - - BackupPolygonIdIdentityState loading() { - return BackupPolygonIdIdentityState( - status: AppStatus.loading, - filePath: filePath, - ); - } - - BackupPolygonIdIdentityState error({ - required MessageHandler messageHandler, - }) { - return BackupPolygonIdIdentityState( - status: AppStatus.error, - filePath: filePath, - message: StateMessage.error(messageHandler: messageHandler), - ); - } - - BackupPolygonIdIdentityState copyWith({ - required AppStatus status, - MessageHandler? messageHandler, - String? filePath, - }) { - return BackupPolygonIdIdentityState( - status: status, - filePath: filePath ?? this.filePath, - message: messageHandler == null - ? null - : StateMessage.success(messageHandler: messageHandler), - ); - } - - Map toJson() => _$BackupPolygonIdIdentityStateToJson(this); - - @override - List get props => [status, filePath, message]; -} diff --git a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart b/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart deleted file mode 100644 index 4cfbf11bb..000000000 --- a/lib/dashboard/drawer/wallet_security/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:altme/activity_log/activity_log.dart'; -import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/drawer/drawer.dart'; -import 'package:altme/l10n/l10n.dart'; -import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/polygon_id/cubit/polygon_id_cubit.dart'; - -import 'package:altme/wallet/wallet.dart'; -import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; -import 'package:file_saver/file_saver.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:secure_storage/secure_storage.dart'; - -class BackupPolygonIdIdentityPage extends StatelessWidget { - const BackupPolygonIdIdentityPage({ - super.key, - }); - - static Route route() { - return MaterialPageRoute( - settings: const RouteSettings(name: '/BackupPolygonIdIdentityPage'), - builder: (_) => const BackupPolygonIdIdentityPage(), - ); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => BackupPolygonIdIdentityCubit( - secureStorageProvider: getSecureStorage, - cryptoKeys: const CryptocurrencyKeys(), - walletCubit: context.read(), - fileSaver: FileSaver.instance, - polygonIdCubit: context.read(), - activityLogManager: ActivityLogManager(getSecureStorage), - ), - child: const BackupPolygonIdIdentityView(), - ); - } -} - -class BackupPolygonIdIdentityView extends StatelessWidget { - const BackupPolygonIdIdentityView({super.key}); - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - - return BasePage( - title: l10n.backupPolygonIdIdentity, - titleAlignment: Alignment.topCenter, - padding: const EdgeInsets.only( - top: 0, - bottom: Sizes.spaceSmall, - left: Sizes.spaceSmall, - right: Sizes.spaceSmall, - ), - titleLeading: BackLeadingButton( - onPressed: () { - if (context.read().state.status != - AppStatus.loading) { - Navigator.of(context).pop(); - } - }, - ), - body: BlocConsumer( - listener: (context, state) async { - if (state.status == AppStatus.loading) { - LoadingView().show(context: context); - } else { - LoadingView().hide(); - } - - if (state.message != null) { - AlertMessage.showStateMessage( - context: context, - stateMessage: state.message!, - ); - //set a delay to sure about showing message - await Future.delayed(const Duration(milliseconds: 800)); - } - - if (state.status == AppStatus.success) { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - } - }, - builder: (context, state) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const MStepper( - totalStep: 2, - step: 2, - ), - const SizedBox(height: Sizes.spaceNormal), - Text( - l10n.saveBackupCredentialTitle, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: Sizes.spaceXLarge), - Text( - l10n.saveBackupPolygonCredentialSubtitle, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(height: Sizes.spaceXLarge), - Image.asset( - ImageStrings.receiveSqure, - width: Sizes.icon6x, - height: Sizes.icon6x, - ), - ], - ); - }, - ), - navigation: Padding( - padding: const EdgeInsets.all(Sizes.spaceSmall), - child: MyElevatedButton( - onPressed: () async { - await context - .read() - .saveEncryptedFile(); - }, - text: l10n.backupCredentialButtonTitle, - ), - ), - ); - } -} diff --git a/lib/dashboard/drawer/wallet_security/backup/src/src.dart b/lib/dashboard/drawer/wallet_security/backup/src/src.dart deleted file mode 100644 index c3954bcfb..000000000 --- a/lib/dashboard/drawer/wallet_security/backup/src/src.dart +++ /dev/null @@ -1 +0,0 @@ -export 'view/backup_menu.dart'; diff --git a/lib/dashboard/drawer/wallet_security/backup/src/view/backup_menu.dart b/lib/dashboard/drawer/wallet_security/backup/src/view/backup_menu.dart deleted file mode 100644 index fe11259ae..000000000 --- a/lib/dashboard/drawer/wallet_security/backup/src/view/backup_menu.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/l10n/l10n.dart'; -import 'package:flutter/material.dart'; - -class BackupMenu extends StatelessWidget { - const BackupMenu({super.key}); - - static Route route() { - return MaterialPageRoute( - settings: const RouteSettings(name: '/BackupMenu'), - builder: (_) => const BackupMenu(), - ); - } - - @override - Widget build(BuildContext context) { - return const BackupView(); - } -} - -class BackupView extends StatelessWidget { - const BackupView({super.key}); - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Drawer( - backgroundColor: Theme.of(context).colorScheme.surface, - child: SafeArea( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const BackLeadingButton( - padding: EdgeInsets.zero, - ), - WalletLogo( - height: 90, - width: MediaQuery.of(context).size.shortestSide * 0.5, - showPoweredBy: true, - ), - DrawerItem( - title: l10n.backupCredential, - onTap: () async { - await securityCheck( - context: context, - localAuthApi: LocalAuthApi(), - onSuccess: () { - Navigator.of(context).push( - BackupMnemonicPage.route( - title: l10n.backupCredential, - isValidCallback: () { - Navigator.of(context) - .push(BackupCredentialPage.route()); - }, - ), - ); - }, - ); - }, - ), - DrawerItem( - title: l10n.backupPolygonIdIdentity, - onTap: () async { - await securityCheck( - context: context, - localAuthApi: LocalAuthApi(), - onSuccess: () { - Navigator.of(context).push( - BackupMnemonicPage.route( - title: l10n.backupPolygonIdIdentity, - isValidCallback: () { - Navigator.of(context).push( - BackupPolygonIdIdentityPage.route(), - ); - }, - ), - ); - }, - ); - }, - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/dashboard/drawer/wallet_security/restore/restore.dart b/lib/dashboard/drawer/wallet_security/restore/restore.dart index c09f122e5..bc996ae1d 100644 --- a/lib/dashboard/drawer/wallet_security/restore/restore.dart +++ b/lib/dashboard/drawer/wallet_security/restore/restore.dart @@ -1,3 +1,2 @@ export 'restore_credential/restore_credential.dart'; export 'restore_credential_mnemonics/restore_credential_mnemonic.dart'; -export 'src/src.dart'; diff --git a/lib/dashboard/drawer/wallet_security/restore/restore_credential/cubit/restore_credential_cubit.dart b/lib/dashboard/drawer/wallet_security/restore/restore_credential/cubit/restore_credential_cubit.dart index 76c8c4c30..5b67c368a 100644 --- a/lib/dashboard/drawer/wallet_security/restore/restore_credential/cubit/restore_credential_cubit.dart +++ b/lib/dashboard/drawer/wallet_security/restore/restore_credential/cubit/restore_credential_cubit.dart @@ -5,7 +5,6 @@ import 'package:altme/activity_log/activity_log.dart'; import 'package:altme/app/app.dart'; import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/dashboard/home/tab_bar/credentials/models/activity/activity.dart'; import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; import 'package:equatable/equatable.dart'; @@ -25,6 +24,7 @@ class RestoreCredentialCubit extends Cubit { required this.secureStorageProvider, required this.polygonId, required this.activityLogManager, + required this.profileCubit, }) : super(const RestoreCredentialState()); final WalletCubit walletCubit; @@ -33,24 +33,43 @@ class RestoreCredentialCubit extends Cubit { final SecureStorageProvider secureStorageProvider; final PolygonId polygonId; final ActivityLogManager activityLogManager; + final ProfileCubit profileCubit; void setFilePath({String? filePath}) { emit(state.copyWith(backupFilePath: filePath)); } - Future recoverWallet({required bool isPolygonIdCredentials}) async { + Future recoverWallet({ + required bool isFromOnBoarding, + }) async { if (state.backupFilePath == null) return; await Future.delayed(const Duration(milliseconds: 500)); emit(state.loading()); - final String? recoveryMnemonic = await secureStorageProvider - .get(SecureStorageKeys.recoverCredentialMnemonics); + late String stringForBackup; - if (recoveryMnemonic == null) { - throw ResponseMessage( - message: - ResponseString.RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER, - ); + if (Parameters.importAndRestoreAtOnboarding) { + final String? recoveryMnemonic = await secureStorageProvider + .get(SecureStorageKeys.recoverCredentialMnemonics); + + if (recoveryMnemonic == null) { + throw ResponseMessage( + message: ResponseString + .RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER, + ); + } + stringForBackup = recoveryMnemonic; + } else { + final String? ssiMnemonic = + await secureStorageProvider.get(SecureStorageKeys.ssiMnemonic); + + if (ssiMnemonic == null) { + throw ResponseMessage( + message: ResponseString + .RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER, + ); + } + stringForBackup = ssiMnemonic; } try { @@ -59,95 +78,128 @@ class RestoreCredentialCubit extends Cubit { late List credentialList; - if (isPolygonIdCredentials) { - final polygonIdNetwork = - await secureStorageProvider.get(SecureStorageKeys.polygonIdNetwork); + final json = jsonDecode(text) as Map; + if (!json.containsKey('cipherText') || + !json.containsKey('authenticationTag') || + json['cipherText'] is! String || + json['authenticationTag'] is! String) { + throw ResponseMessage( + message: ResponseString + .RESPONSE_STRING_RECOVERY_CREDENTIAL_JSON_FORMAT_ERROR_MESSAGE, + ); + } + final encryption = Encryption( + cipherText: json['cipherText'] as String, + authenticationTag: json['authenticationTag'] as String, + ); + final decryptedText = + await cryptoKeys.decrypt(stringForBackup, encryption); + final decryptedJson = jsonDecode(decryptedText) as Map; + if (!decryptedJson.containsKey('date') || + !decryptedJson.containsKey('credentials') || + decryptedJson['date'] is! String) { + throw ResponseMessage( + message: ResponseString + .RESPONSE_STRING_RECOVERY_CREDENTIAL_JSON_FORMAT_ERROR_MESSAGE, + ); + } - String network = Parameters.POLYGON_MAIN_NETWORK; + final List credentialJson = + decryptedJson['credentials'] as List; + final credentials = credentialJson.map( + (dynamic credential) => + CredentialModel.fromJson(credential as Map), + ); - if (polygonIdNetwork == PolygonIdNetwork.PolygonMainnet.toString()) { - network = Parameters.POLYGON_MAIN_NETWORK; - } else { - network = Parameters.POLYGON_TEST_NETWORK; - } + credentialList = credentials.toList(); - final privateIdentityEntity = await polygonId.restoreIdentity( - mnemonic: recoveryMnemonic, - encryptedDb: text, - network: network, - ); + await credentialsCubit.recoverWallet(credentials: credentialList); - final List claims = await polygonId.restoreClaims( - privateIdentityEntity: privateIdentityEntity, - ); + final profile = decryptedJson['profile']; - final credentials = claims.map( - (ClaimEntity claimEntity) { - final jsonCredential = claimEntity.info; - final credentialPreview = Credential.fromJson(jsonCredential); - - final credentialModel = CredentialModel( - id: claimEntity.id, - image: 'image', - data: jsonCredential, - shareLink: '', - credentialPreview: credentialPreview, - expirationDate: claimEntity.expiration, - jwt: null, - format: 'ldp_vc', - activities: [Activity(acquisitionAt: DateTime.now())], - ); - return credentialModel; - }, - ); - credentialList = credentials.toList(); - } else { - final json = jsonDecode(text) as Map; - if (!json.containsKey('cipherText') || - !json.containsKey('authenticationTag') || - json['cipherText'] is! String || - json['authenticationTag'] is! String) { - throw ResponseMessage( - message: ResponseString - .RESPONSE_STRING_RECOVERY_CREDENTIAL_JSON_FORMAT_ERROR_MESSAGE, - ); - } - final encryption = Encryption( - cipherText: json['cipherText'] as String, - authenticationTag: json['authenticationTag'] as String, - ); - final decryptedText = - await cryptoKeys.decrypt(recoveryMnemonic, encryption); - final decryptedJson = jsonDecode(decryptedText) as Map; - if (!decryptedJson.containsKey('date') || - !decryptedJson.containsKey('credentials') || - decryptedJson['date'] is! String) { + if (profile != null) { + final profileType = ProfileType.values + .firstWhereOrNull((ele) => ele.profileId == profile); + + if (profileType == null) { throw ResponseMessage( - message: ResponseString - .RESPONSE_STRING_RECOVERY_CREDENTIAL_JSON_FORMAT_ERROR_MESSAGE, + message: ResponseString.RESPONSE_STRING_AN_UNKNOWN_ERROR_HAPPENED, ); } - final List credentialJson = - decryptedJson['credentials'] as List; - final credentials = credentialJson.map( - (dynamic credential) => - CredentialModel.fromJson(credential as Map), - ); + if (profileType == ProfileType.custom || + profileType == ProfileType.enterprise) { + final profileSettingJson = decryptedJson['profileSetting']; + + if (profileSettingJson != null) { + final profileSetting = ProfileSetting.fromJson( + profileSettingJson as Map, + ); + await profileCubit.setProfileSetting( + profileSetting: profileSetting, + profileType: profileType, + ); - credentialList = credentials.toList(); + if (profileType == ProfileType.custom) { + await secureStorageProvider.set( + SecureStorageKeys.customProfileSettings, + jsonEncode(profileSettingJson), + ); + } + } + } else { + await profileCubit.setProfile(profileType); + } + + if (profileType == ProfileType.enterprise) { + final enterprise = decryptedJson['enterprise']; + if (enterprise != null) { + final email = enterprise['email']; + final password = enterprise['password']; + final walletProvider = enterprise['walletProvider']; + + if (email != null) { + await profileCubit.secureStorageProvider + .set(SecureStorageKeys.enterpriseEmail, email.toString()); + } + + if (password != null) { + await profileCubit.secureStorageProvider.set( + SecureStorageKeys.enterprisePassword, + password.toString(), + ); + } + + if (walletProvider != null) { + await profileCubit.secureStorageProvider.set( + SecureStorageKeys.enterpriseWalletProvider, + walletProvider.toString(), + ); + } + } + } } - await credentialsCubit.recoverWallet( - credentials: credentialList, - isPolygonIdCredentials: isPolygonIdCredentials, - ); - await credentialsCubit.loadAllCredentials( - blockchainType: walletCubit.state.currentAccount!.blockchainType, - ); + /// restore profile if it is enterprise account + + if (walletCubit.state.currentAccount != null) { + await credentialsCubit.loadAllCredentials( + blockchainType: walletCubit.state.currentAccount!.blockchainType, + ); + } await activityLogManager.saveLog(LogData(type: LogType.restoreWallet)); - emit(state.success(recoveredCredentialLength: credentialList.length)); + + if (isFromOnBoarding) { + emit( + state.copyWith( + status: AppStatus.restoreWallet, + recoveredCredentialLength: credentialList.length, + ), + ); + } else { + emit(state.success(recoveredCredentialLength: credentialList.length)); + } } catch (e) { if (e is MessageHandler) { emit(state.error(messageHandler: e)); diff --git a/lib/dashboard/drawer/wallet_security/restore/restore_credential/restore_credential.dart b/lib/dashboard/drawer/wallet_security/restore/restore_credential/restore_credential.dart index 48af3f3cc..cc0c96a1a 100644 --- a/lib/dashboard/drawer/wallet_security/restore/restore_credential/restore_credential.dart +++ b/lib/dashboard/drawer/wallet_security/restore/restore_credential/restore_credential.dart @@ -1,4 +1,3 @@ export 'cubit/restore_credential_cubit.dart'; export 'view/restore_credential_page.dart'; -export 'view/restore_polygonid_credential_page.dart'; export 'widgets/widgets.dart'; diff --git a/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_credential_page.dart b/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_credential_page.dart index 149e4c3db..2fc0e3461 100644 --- a/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_credential_page.dart +++ b/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_credential_page.dart @@ -19,10 +19,18 @@ import 'package:polygonid/polygonid.dart'; import 'package:secure_storage/secure_storage.dart'; class RestoreCredentialPage extends StatelessWidget { - const RestoreCredentialPage({super.key}); + const RestoreCredentialPage({ + super.key, + required this.fromOnBoarding, + }); - static Route route() => MaterialPageRoute( - builder: (context) => const RestoreCredentialPage(), + final bool fromOnBoarding; + + static Route route({required bool fromOnBoarding}) => + MaterialPageRoute( + builder: (context) => RestoreCredentialPage( + fromOnBoarding: fromOnBoarding, + ), settings: const RouteSettings(name: '/RestoreCredentialPage'), ); @@ -36,14 +44,20 @@ class RestoreCredentialPage extends StatelessWidget { credentialsCubit: context.read(), polygonId: PolygonId(), activityLogManager: ActivityLogManager(getSecureStorage), + profileCubit: context.read(), ), - child: const RestoreCredentialView(), + child: RestoreCredentialView(fromOnBoarding: fromOnBoarding), ); } } class RestoreCredentialView extends StatefulWidget { - const RestoreCredentialView({super.key}); + const RestoreCredentialView({ + super.key, + required this.fromOnBoarding, + }); + + final bool fromOnBoarding; @override _RestoreCredentialViewState createState() => _RestoreCredentialViewState(); @@ -58,102 +72,134 @@ class _RestoreCredentialViewState extends State { @override Widget build(BuildContext context) { final l10n = context.l10n; - return BasePage( - title: l10n.restoreCredential, - titleAlignment: Alignment.topCenter, - padding: const EdgeInsets.only( - top: 0, - left: Sizes.spaceSmall, - right: Sizes.spaceSmall, - bottom: Sizes.spaceSmall, - ), - titleLeading: BackLeadingButton( - onPressed: () { - if (context.read().state.status != - AppStatus.loading) { - Navigator.of(context).pop(); - } - }, - ), - body: BlocConsumer( - listener: (context, state) async { - if (state.status == AppStatus.loading) { - LoadingView().show(context: context); - } else { - LoadingView().hide(); - } - - if (state.status == AppStatus.success && - state.recoveredCredentialLength != null) { - final credentialLength = state.recoveredCredentialLength; - AlertMessage.showStateMessage( - context: context, - stateMessage: StateMessage.success( - stringMessage: l10n.recoveryCredentialSuccessMessage( - '''$credentialLength ${credentialLength! > 1 ? '${l10n.credential}s' : l10n.credential}''', - ), - ), - ); - await Future.delayed(const Duration(milliseconds: 800)); - Navigator.of(context).pop(); - Navigator.of(context).pop(); - } - - if (state.message != null) { - AlertMessage.showStateMessage( - context: context, - stateMessage: state.message!, - ); - } - }, - builder: (context, state) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - const MStepper( - totalStep: 2, - step: 2, - ), - const SizedBox( - height: Sizes.spaceNormal, - ), - Text( - l10n.restoreCredentialStep2Title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.labelMedium, - ), - const SizedBox( - height: Sizes.spaceNormal, - ), - UploadFile( - filePath: state.backupFilePath, - onTap: () async { - if (isAndroid) { - final appDir = (await getTemporaryDirectory()).path; - await Directory(appDir).delete(recursive: true); + return WillPopScope( + onWillPop: () async => false, + child: BasePage( + title: l10n.restoreCredential, + titleAlignment: Alignment.topCenter, + padding: const EdgeInsets.only( + top: 0, + left: Sizes.spaceSmall, + right: Sizes.spaceSmall, + bottom: Sizes.spaceSmall, + ), + titleLeading: widget.fromOnBoarding + ? null + : BackLeadingButton( + onPressed: () { + if (context.read().state.status != + AppStatus.loading) { + Navigator.of(context).pop(); } - await _pickRestoreFile(); }, ), - ], - ); - }, - ), - navigation: Padding( - padding: const EdgeInsets.all(Sizes.spaceSmall), - child: BlocBuilder( + body: BlocConsumer( + listener: (context, state) async { + if (state.status == AppStatus.loading) { + LoadingView().show(context: context); + } else { + LoadingView().hide(); + } + + if (state.status == AppStatus.success && + state.recoveredCredentialLength != null) { + final credentialLength = state.recoveredCredentialLength; + AlertMessage.showStateMessage( + context: context, + stateMessage: StateMessage.success( + stringMessage: l10n.recoveryCredentialSuccessMessage( + '''$credentialLength ${credentialLength! > 1 ? '${l10n.credential}s' : l10n.credential}''', + ), + ), + ); + await Future.delayed(const Duration(milliseconds: 800)); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + } + + if (state.status == AppStatus.restoreWallet) { + await Navigator.pushAndRemoveUntil( + context, + WalletReadyPage.route(), + (Route route) => route.isFirst, + ); + } + + if (state.message != null) { + AlertMessage.showStateMessage( + context: context, + stateMessage: state.message!, + ); + } + }, builder: (context, state) { - return MyElevatedButton( - onPressed: state.backupFilePath == null - ? null - : () => context.read().recoverWallet( - isPolygonIdCredentials: false, - ), - text: l10n.loadFile, + return Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + const MStepper( + totalStep: 2, + step: 2, + ), + const SizedBox( + height: Sizes.spaceNormal, + ), + Text( + l10n.restoreCredentialStep2Title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelMedium, + ), + const SizedBox( + height: Sizes.spaceNormal, + ), + UploadFile( + filePath: state.backupFilePath, + onTap: () async { + if (isAndroid) { + final appDir = (await getTemporaryDirectory()).path; + await Directory(appDir).delete(recursive: true); + } + await _pickRestoreFile(); + }, + ), + ], ); }, ), + navigation: Padding( + padding: const EdgeInsets.all(Sizes.spaceSmall), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + BlocBuilder( + builder: (context, state) { + return MyElevatedButton( + onPressed: state.backupFilePath == null + ? null + : () => context + .read() + .recoverWallet( + isFromOnBoarding: widget.fromOnBoarding, + ), + text: l10n.loadFile, + ); + }, + ), + const SizedBox(height: 5), + MyOutlinedButton( + text: l10n.skip, + onPressed: () { + Navigator.pushAndRemoveUntil( + context, + WalletReadyPage.route(), + (Route route) => route.isFirst, + ); + }, + ), + ], + ), + ), ), ); } diff --git a/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_polygonid_credential_page.dart b/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_polygonid_credential_page.dart deleted file mode 100644 index 356ddc318..000000000 --- a/lib/dashboard/drawer/wallet_security/restore/restore_credential/view/restore_polygonid_credential_page.dart +++ /dev/null @@ -1,209 +0,0 @@ -import 'dart:io'; - -import 'package:altme/activity_log/activity_log.dart'; -import 'package:altme/app/app.dart'; -import 'package:altme/credentials/credentials.dart'; -import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/l10n/l10n.dart'; -import 'package:altme/onboarding/onboarding.dart'; - -import 'package:altme/wallet/wallet.dart'; -import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:polygonid/polygonid.dart'; -import 'package:secure_storage/secure_storage.dart'; - -class RestorePolygonIdCredentialPage extends StatelessWidget { - const RestorePolygonIdCredentialPage({super.key}); - - static Route route() => MaterialPageRoute( - builder: (context) => const RestorePolygonIdCredentialPage(), - settings: const RouteSettings(name: '/RestorePolygonIdCredentialPage'), - ); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => RestoreCredentialCubit( - secureStorageProvider: getSecureStorage, - cryptoKeys: const CryptocurrencyKeys(), - walletCubit: context.read(), - credentialsCubit: context.read(), - polygonId: PolygonId(), - activityLogManager: ActivityLogManager(getSecureStorage), - ), - child: const RestorePolygonIdCredentialView(), - ); - } -} - -class RestorePolygonIdCredentialView extends StatefulWidget { - const RestorePolygonIdCredentialView({super.key}); - - @override - _RestorePolygonIdCredentialViewState createState() => - _RestorePolygonIdCredentialViewState(); -} - -class _RestorePolygonIdCredentialViewState - extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return BasePage( - title: l10n.restorePolygonIdCredentials, - titleAlignment: Alignment.topCenter, - padding: const EdgeInsets.only( - top: 0, - left: Sizes.spaceSmall, - right: Sizes.spaceSmall, - bottom: Sizes.spaceSmall, - ), - titleLeading: BackLeadingButton( - onPressed: () { - if (context.read().state.status != - AppStatus.loading) { - Navigator.of(context).pop(); - } - }, - ), - body: BlocConsumer( - listener: (context, state) async { - if (state.status == AppStatus.loading) { - LoadingView().show(context: context); - } else { - LoadingView().hide(); - } - - if (state.status == AppStatus.success && - state.recoveredCredentialLength != null) { - final credentialLength = state.recoveredCredentialLength; - AlertMessage.showStateMessage( - context: context, - stateMessage: StateMessage.success( - stringMessage: l10n.recoveryCredentialSuccessMessage( - '''$credentialLength ${credentialLength! > 1 ? '${l10n.credential}s' : l10n.credential}''', - ), - ), - ); - await Future.delayed(const Duration(milliseconds: 800)); - Navigator.of(context).pop(); - Navigator.of(context).pop(); - } - - if (state.message != null) { - AlertMessage.showStateMessage( - context: context, - stateMessage: state.message!, - ); - } - }, - builder: (context, state) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - const MStepper( - totalStep: 2, - step: 2, - ), - const SizedBox( - height: Sizes.spaceNormal, - ), - Text( - l10n.restoreCredentialStep2Title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.labelMedium, - ), - const SizedBox( - height: Sizes.spaceNormal, - ), - UploadFile( - filePath: state.backupFilePath, - onTap: () async { - if (Platform.isAndroid) { - final appDir = (await getTemporaryDirectory()).path; - await Directory(appDir).delete(recursive: true); - } - await _pickRestoreFile(); - }, - ), - ], - ); - }, - ), - navigation: Padding( - padding: const EdgeInsets.all(Sizes.spaceSmall), - child: BlocBuilder( - builder: (context, state) { - return MyElevatedButton( - onPressed: state.backupFilePath == null - ? null - : () => context.read().recoverWallet( - isPolygonIdCredentials: true, - ), - text: l10n.loadFile, - ); - }, - ), - ), - ); - } - - Future _pickRestoreFile() async { - final l10n = context.l10n; - final storagePermission = await Permission.storage.request(); - if (storagePermission.isDenied) { - AlertMessage.showStateMessage( - context: context, - stateMessage: StateMessage.success( - stringMessage: l10n.storagePermissionDeniedMessage, - ), - ); - return; - } - - if (storagePermission.isPermanentlyDenied) { - await _showPermissionPopup(); - return; - } - - if (storagePermission.isGranted || storagePermission.isLimited) { - final pickedFile = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowMultiple: false, - allowedExtensions: ['txt'], - ); - context - .read() - .setFilePath(filePath: pickedFile?.files.first.path); - } - } - - Future _showPermissionPopup() async { - final localizations = context.l10n; - final confirm = await showDialog( - context: context, - builder: (context) => ConfirmDialog( - title: localizations.storagePermissionRequired, - subtitle: localizations.storagePermissionPermanentlyDeniedMessage, - yes: localizations.ok, - no: localizations.cancel, - ), - ) ?? - false; - - if (confirm) { - await openAppSettings(); - } - } -} diff --git a/lib/dashboard/drawer/wallet_security/restore/src/src.dart b/lib/dashboard/drawer/wallet_security/restore/src/src.dart deleted file mode 100644 index 9212bf7a8..000000000 --- a/lib/dashboard/drawer/wallet_security/restore/src/src.dart +++ /dev/null @@ -1 +0,0 @@ -export 'view/restore_menu.dart'; diff --git a/lib/dashboard/drawer/wallet_security/restore/src/view/restore_menu.dart b/lib/dashboard/drawer/wallet_security/restore/src/view/restore_menu.dart deleted file mode 100644 index d51db8222..000000000 --- a/lib/dashboard/drawer/wallet_security/restore/src/view/restore_menu.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/l10n/l10n.dart'; -import 'package:flutter/material.dart'; - -class RestoreMenu extends StatelessWidget { - const RestoreMenu({super.key}); - - static Route route() { - return MaterialPageRoute( - settings: const RouteSettings(name: '/RestoreMenu'), - builder: (_) => const RestoreMenu(), - ); - } - - @override - Widget build(BuildContext context) { - return const RestoreView(); - } -} - -class RestoreView extends StatelessWidget { - const RestoreView({super.key}); - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Drawer( - backgroundColor: Theme.of(context).colorScheme.surface, - child: SafeArea( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const BackLeadingButton( - padding: EdgeInsets.zero, - ), - WalletLogo( - height: 90, - width: MediaQuery.of(context).size.shortestSide * 0.5, - showPoweredBy: true, - ), - DrawerItem( - title: l10n.restoreCredential, - onTap: () async { - final confirm = await showDialog( - context: context, - builder: (context) => ConfirmDialog( - title: l10n.warningDialogTitle, - subtitle: - l10n.restorationCredentialWarningDialogSubtitle, - yes: l10n.showDialogYes, - no: l10n.showDialogNo, - ), - ) ?? - false; - - if (confirm) { - await securityCheck( - context: context, - localAuthApi: LocalAuthApi(), - onSuccess: () { - Navigator.of(context).push( - RestoreCredentialMnemonicPage.route( - title: l10n.restoreCredential, - isValidCallback: () { - Navigator.of(context).push( - RestoreCredentialPage.route(), - ); - }, - ), - ); - }, - ); - } - }, - ), - DrawerItem( - title: l10n.restorePolygonIdCredentials, - onTap: () { - securityCheck( - context: context, - localAuthApi: LocalAuthApi(), - onSuccess: () { - Navigator.of(context).push( - RestoreCredentialMnemonicPage.route( - title: l10n.restorePolygonIdCredentials, - isValidCallback: () { - Navigator.of(context).push( - RestorePolygonIdCredentialPage.route(), - ); - }, - ), - ); - }, - ); - }, - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart b/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart index e1f0ca806..1fa8698cb 100644 --- a/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart +++ b/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart @@ -60,7 +60,9 @@ class WalletSecurityView extends StatelessWidget { ), DrawerItem( title: l10n.showWalletRecoveryPhrase, - subtitle: l10n.showWalletRecoveryPhraseSubtitle, + subtitle: Parameters.importAndRestoreAtOnboarding + ? l10n.showWalletRecoveryPhraseSubtitle + : l10n.showWalletRecoveryPhraseSubtitle2, onTap: () async { final confirm = await showDialog( context: context, @@ -98,15 +100,60 @@ class WalletSecurityView extends StatelessWidget { DrawerItem( title: l10n.backup, onTap: () async { - await Navigator.of(context).push(BackupMenu.route()); - }, - ), - DrawerItem( - title: l10n.restore, - onTap: () async { - await Navigator.of(context).push(RestoreMenu.route()); + await securityCheck( + context: context, + localAuthApi: LocalAuthApi(), + onSuccess: () { + Navigator.of(context).push( + BackupMnemonicPage.route( + title: l10n.backupCredential, + isValidCallback: () { + Navigator.of(context) + .push(BackupCredentialPage.route()); + }, + ), + ); + }, + ); }, ), + // DrawerItem( + // title: l10n.restore, + // onTap: () async { + // final confirm = await showDialog( + // context: context, + // builder: (context) => ConfirmDialog( + // title: l10n.warningDialogTitle, + // subtitle: + // l10n.restorationCredentialWarningDialogSubtitle, + // yes: l10n.showDialogYes, + // no: l10n.showDialogNo, + // ), + // ) ?? + // false; + + // if (confirm) { + // await securityCheck( + // context: context, + // localAuthApi: LocalAuthApi(), + // onSuccess: () { + // Navigator.of(context).push( + // RestoreCredentialMnemonicPage.route( + // title: l10n.restoreCredential, + // isValidCallback: () { + // Navigator.of(context).push( + // RestoreCredentialPage.route( + // fromOnBoarding: false, + // ), + // ); + // }, + // ), + // ); + // }, + // ); + // } + // }, + // ), ], ), ); diff --git a/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart b/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart index 0d41b05ed..bbb1ed1a7 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart @@ -1,4 +1,7 @@ +import 'dart:convert'; + import 'package:altme/app/app.dart'; +import 'package:altme/app/shared/helper_functions/get_display.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/lang/cubit/lang_cubit.dart'; import 'package:flutter/material.dart'; @@ -80,41 +83,22 @@ class CredentialSubjectData extends StatelessWidget { if (value is! Map) return Container(); - // if (value.isEmpty) return Container(); - if (value.containsKey('display')) { - final displays = value['display']; - if (displays is! List) return Container(); - - final display = displays.firstWhere( - (element) => - element is Map && - element.containsKey('locale') && - element['locale'].toString().contains(languageCode), - orElse: () => displays.firstWhere( - (element) => - element is Map && - element.containsKey('locale') && - element['locale'].toString().contains('en'), - orElse: () => displays.firstWhere( - (element) => - element is Map && - element.containsKey('locale'), - orElse: () => null, - ), - ), - ); - + final display = getDisplay(value, languageCode); if (display == null) return Container(); if (credentialSubjectData.containsKey(key)) { title = display['name'].toString(); - data = credentialSubjectData[key].toString(); + data = credentialSubjectData[key] is Map + ? jsonEncode(credentialSubjectData[key]) + : credentialSubjectData[key].toString(); } } else { if (credentialSubjectData[key] != null) { title = null; - data = credentialSubjectData[key].toString(); + data = credentialSubjectData[key] is Map + ? jsonEncode(credentialSubjectData[key]) + : credentialSubjectData[key].toString(); } else { return Container(); } @@ -122,16 +106,158 @@ class CredentialSubjectData extends StatelessWidget { if (data == null) return Container(); - return CredentialField( + final widget = DisplayCredentialField( + title: title, + data: data, + type: value['value_type'].toString(), + showVertically: showVertically, + ); + + return widget; + }).toList(), + ); + } +} + +class DisplayCredentialField extends StatelessWidget { + const DisplayCredentialField({ + super.key, + this.title, + required this.data, + this.type = 'string', + required this.showVertically, + }); + final String? title; + final dynamic data; + final String type; + final bool showVertically; + + @override + Widget build(BuildContext context) { + Widget? widget; + try { + final json = jsonDecode(data.toString()); + if (json is Map) { + widget = IndentedCredentialFields( + title: title, + children: DisplayCredentialFieldMap( + showVertically: showVertically, + data: json, + type: type, + ), + ); + } + + if (json is List) { + widget = IndentedCredentialFields( + title: title, + children: DisplayCredentialFieldList( + showVertically: showVertically, + data: json, + type: type, + ), + ); + } + } catch (e) { + /// Empty catch because data is not a valid json and + /// the return of the function will take care of + /// this usecase (widget is null) + } + + return widget ?? + CredentialField( padding: const EdgeInsets.only(top: 10), title: title, - value: data, + value: data.toString(), titleColor: Theme.of(context).colorScheme.onSurface, valueColor: Theme.of(context).colorScheme.onSurface, showVertically: showVertically, - type: value['value_type'].toString(), + type: type, ); - }).toList(), + } + + List DisplayCredentialFieldMap({ + required bool showVertically, + required Map data, + required String type, + }) { + final List column = []; + + /// for each element in Map data, call DisplayCredentialField + for (final element in data.entries) { + column.add( + DisplayCredentialField( + title: element.key, + data: element.value is String + ? element.value + : jsonEncode(element.value), + type: type, + showVertically: showVertically, + ), + ); + } + + return column; + } + + List DisplayCredentialFieldList({ + required bool showVertically, + required List data, + required String type, + }) { + final List column = []; + + /// for each element in Map data, call DisplayCredentialField + for (final element in data) { + column.add( + DisplayCredentialField( + title: null, + data: element is String ? element : jsonEncode(element), + type: type, + showVertically: showVertically, + ), + ); + } + + return column; + } +} + +class IndentedCredentialFields extends StatelessWidget { + const IndentedCredentialFields({ + super.key, + required this.children, + this.title, + }); + + final List children; + final String? title; + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title != null) + Padding( + padding: const EdgeInsets.only(top: 10), + child: Text( + title!, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + ) + else + const SizedBox.shrink(), + Padding( + padding: const EdgeInsets.only(left: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ), + ], ); } } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/filter_credential_list_by_format.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/filter_credential_list_by_format.dart index c9c891da8..afad99e4f 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/filter_credential_list_by_format.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/filter_credential_list_by_format.dart @@ -10,51 +10,24 @@ List filterCredenialListByFormat({ required PresentationDefinition presentationDefinition, required List filterList, }) { - if (vcFormatType == VCFormatType.auto) { - return credentialList; - } - final credentials = List.from(credentialList); if (filterList.isNotEmpty) { - final isJwtVpInJwtVCRequired = presentationDefinition.format?.jwtVp != null; - - if (isJwtVpInJwtVCRequired) { - credentials.removeWhere( - (CredentialModel credentialModel) => credentialModel.jwt == null, - ); - } - - final (presentLdpVc, presentJwtVc, presentJwtVcJson, presentVcSdJwt) = - getPresentVCDetails( + final supportingFormats = getPresentVCDetails( clientMetaData: clientMetaData, presentationDefinition: presentationDefinition, vcFormatType: vcFormatType, credentialsToBePresented: credentials, ); - credentials.removeWhere( (CredentialModel credentialModel) { - /// remove ldpVc - if (presentLdpVc) { - return credentialModel.getFormat != VCFormatType.ldpVc.vcValue; - } - - /// remove jwtVc - if (presentJwtVc) { - return credentialModel.getFormat != VCFormatType.jwtVc.vcValue; - } - - /// remove JwtVcJson - if (presentJwtVcJson) { - return credentialModel.getFormat != VCFormatType.jwtVcJson.vcValue; + /// we keep credential whose format are supported + bool remove = true; + for (final supportingFormat in supportingFormats) { + if (credentialModel.getFormat == supportingFormat.vcValue) { + remove = false; + } } - - /// remove vcSdJwt - if (presentVcSdJwt) { - return credentialModel.getFormat != VCFormatType.vcSdJWT.vcValue; - } - - return false; + return remove; }, ); } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart index 368745eab..dc6bf2b45 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart @@ -189,8 +189,11 @@ class CredentialManifestOfferPickView extends StatelessWidget { ? SafeArea( child: Container( padding: const EdgeInsets.all(16), - child: isVcSdJWT - ? MyElevatedButton( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (isVcSdJWT) + MyElevatedButton( onPressed: !credentialManifestState .isButtonEnabled ? null @@ -220,7 +223,8 @@ class CredentialManifestOfferPickView extends StatelessWidget { /// from the selected credential text: l10n.next, ) - : Builder( + else + Builder( builder: (context) { final inputDescriptor = presentationDefinition! @@ -277,6 +281,13 @@ class CredentialManifestOfferPickView extends StatelessWidget { } }, ), + const SizedBox(height: 8), + MyOutlinedButton( + text: l10n.cancel, + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), ), ) : const SizedBox.shrink(), diff --git a/lib/dashboard/profile/models/profile.dart b/lib/dashboard/profile/models/profile.dart index f31c5309a..067113f8a 100644 --- a/lib/dashboard/profile/models/profile.dart +++ b/lib/dashboard/profile/models/profile.dart @@ -282,7 +282,7 @@ class ProfileModel extends Equatable { defaultDid: Parameters.didKeyTypeForDutch, oidc4vciDraft: OIDC4VCIDraftType.draft13, oidc4vpDraft: OIDC4VPDraftType.draft10, - scope: false, + scope: true, securityLevel: true, proofHeader: ProofHeaderType.kid, siopv2Draft: SIOPV2DraftType.draft12, @@ -356,7 +356,7 @@ class ProfileModel extends Equatable { defaultDid: Parameters.didKeyTypeForOwfBaselineProfile, oidc4vciDraft: OIDC4VCIDraftType.draft13, oidc4vpDraft: OIDC4VPDraftType.draft18, - scope: false, + scope: true, securityLevel: true, proofHeader: ProofHeaderType.kid, siopv2Draft: SIOPV2DraftType.draft12, diff --git a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart index fd2ebd1de..8e66974fe 100644 --- a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart +++ b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart @@ -1318,6 +1318,8 @@ class QRCodeScanCubit extends Cubit { oAuthClientAttestationPop: oAuthClientAttestationPop, secureAuthorizedFlow: customOidc4vcProfile.pushAuthorizationRequest, client: client, + profileType: profileCubit.state.model.profileType, + walletIssuer: Parameters.walletIssuer, ); goBack(); } diff --git a/lib/dashboard/user_pin/view/user_pin_page.dart b/lib/dashboard/user_pin/view/user_pin_page.dart index 8b45b9fdd..49b237c98 100644 --- a/lib/dashboard/user_pin/view/user_pin_page.dart +++ b/lib/dashboard/user_pin/view/user_pin_page.dart @@ -100,7 +100,8 @@ class _UserPinViewState extends State { style: Theme.of(context).textTheme.labelLarge, ), cancelCallback: _onPasscodeCancelled, - showKeyboard: widget.txCode?.inputMode != 'numeric', + showKeyboard: + widget.txCode != null && widget.txCode!.inputMode != 'numeric', isValidCallback: () { Navigator.pop(context); widget.onProceed.call( diff --git a/lib/enterprise/cubit/enterprise_cubit.dart b/lib/enterprise/cubit/enterprise_cubit.dart index 40bda715f..dea17c087 100644 --- a/lib/enterprise/cubit/enterprise_cubit.dart +++ b/lib/enterprise/cubit/enterprise_cubit.dart @@ -52,16 +52,6 @@ class EnterpriseCubit extends Cubit { ); } - final savedEmail = await profileCubit.secureStorageProvider.get( - SecureStorageKeys.enterpriseEmail, - ); - - if (savedEmail != null) { - throw ResponseMessage( - message: ResponseString.RESPONSE_STRING_thisWalleIsAlreadyConfigured, - ); - } - /// get vc and store it in the wallet final walletAttestationData = await getWalletAttestationData(url); @@ -114,19 +104,51 @@ class EnterpriseCubit extends Cubit { ); final profileSettingJson = - profileCubit.jwtDecode.parseJwt(response as String); - // we emit new state, waiting for user approval - emit( - state.copyWith( - status: AppStatus.walletProviderApproval, - profileSettingJson: jsonEncode(profileSettingJson), - ), + jsonEncode(profileCubit.jwtDecode.parseJwt(response as String)); + + final savedEmail = await profileCubit.secureStorageProvider.get( + SecureStorageKeys.enterpriseEmail, ); + + // we emit new state, waiting for user approval + + if (savedEmail == null) { + // new configuration + + // throw ResponseMessage( + // message: ResponseString.RESPONSE_STRING_thisWalleIsAlreadyConfigured, + // ); + emit( + state.copyWith( + status: AppStatus.addEnterpriseAccount, + profileSettingJson: profileSettingJson, + ), + ); + } else { + if (email == savedEmail) { + // update old configuraion + emit( + state.copyWith( + status: AppStatus.updateEnterpriseAccount, + profileSettingJson: profileSettingJson, + ), + ); + } else { + // new configuration + emit( + state.copyWith( + status: AppStatus.replaceEnterpriseAccount, + profileSettingJson: profileSettingJson, + ), + ); + } + } } - Future applyConfiguration( - QRCodeScanCubit qrCodeScanCubit, - ) async { + Future applyConfiguration({ + required QRCodeScanCubit qrCodeScanCubit, + required AppStatus status, + }) async { assert(state.profileSettingJson != null, 'Profile setting is missing.'); emit(state.loading()); @@ -177,8 +199,11 @@ class EnterpriseCubit extends Cubit { status: AppStatus.success, message: StateMessage.success( messageHandler: ResponseMessage( - message: ResponseString - .RESPONSE_STRING_successfullyAddedEnterpriseAccount, + message: status == AppStatus.addEnterpriseAccount + ? ResponseString + .RESPONSE_STRING_successfullyAddedEnterpriseAccount + : ResponseString + .RESPONSE_STRING_successfullyUpdatedEnterpriseAccount, ), ), ), diff --git a/lib/import_wallet/cubit/import_wallet_cubit.dart b/lib/import_wallet/cubit/import_wallet_cubit.dart index 91cbec46e..804c03156 100644 --- a/lib/import_wallet/cubit/import_wallet_cubit.dart +++ b/lib/import_wallet/cubit/import_wallet_cubit.dart @@ -51,13 +51,15 @@ class ImportWalletCubit extends Cubit { Future import({ required String mnemonicOrKey, - required bool isFromOnboarding, + required RestoreType? restoreType, String? accountName, }) async { final log = getLogger('ImportWalletCubit - import'); emit(state.loading()); await Future.delayed(const Duration(milliseconds: 500)); + final isFromOnboarding = restoreType != null; + try { log.i('isFromOnboarding: $isFromOnboarding'); if (isFromOnboarding) { @@ -122,6 +124,7 @@ class ImportWalletCubit extends Cubit { await activityLogManager.saveLog(LogData(type: LogType.importKey)); await homeCubit.emitHasWallet(); + emit(state.success()); } catch (e, s) { log.e( diff --git a/lib/import_wallet/cubit/import_wallet_state.dart b/lib/import_wallet/cubit/import_wallet_state.dart index b245e4a01..3492c1241 100644 --- a/lib/import_wallet/cubit/import_wallet_state.dart +++ b/lib/import_wallet/cubit/import_wallet_state.dart @@ -46,6 +46,20 @@ class ImportWalletState extends Equatable { ); } + ImportWalletState copyWith({ + required AppStatus status, + MessageHandler? messageHandler, + }) { + return ImportWalletState( + status: status, + message: messageHandler == null + ? null + : StateMessage.success(messageHandler: messageHandler), + isTextFieldEdited: isTextFieldEdited, + isMnemonicOrKeyValid: isMnemonicOrKeyValid, + ); + } + ImportWalletState success({ MessageHandler? messageHandler, }) { diff --git a/lib/import_wallet/view/import_from_wallet_page.dart b/lib/import_wallet/view/import_from_wallet_page.dart index 94ef331a9..c06b76e68 100644 --- a/lib/import_wallet/view/import_from_wallet_page.dart +++ b/lib/import_wallet/view/import_from_wallet_page.dart @@ -18,26 +18,26 @@ class ImportFromWalletPage extends StatelessWidget { super.key, required this.walletTypeModel, this.accountName, - required this.isFromOnboard, + required this.restoreType, }); static Route route({ required WalletTypeModel walletTypeModel, - required bool isFromOnboard, + required RestoreType? restoreType, String? accountName, }) => MaterialPageRoute( builder: (context) => ImportFromWalletPage( walletTypeModel: walletTypeModel, accountName: accountName, - isFromOnboard: isFromOnboard, + restoreType: restoreType, ), settings: const RouteSettings(name: '/ImportFromWalletPage'), ); final WalletTypeModel walletTypeModel; final String? accountName; - final bool isFromOnboard; + final RestoreType? restoreType; @override Widget build(BuildContext context) { @@ -54,7 +54,7 @@ class ImportFromWalletPage extends StatelessWidget { child: ImportFromOtherWalletView( walletTypeModel: walletTypeModel, accountName: accountName, - isFromOnboard: isFromOnboard, + restoreType: restoreType, ), ); } @@ -64,13 +64,13 @@ class ImportFromOtherWalletView extends StatefulWidget { const ImportFromOtherWalletView({ super.key, required this.walletTypeModel, - required this.isFromOnboard, + required this.restoreType, this.accountName, }); final WalletTypeModel walletTypeModel; final String? accountName; - final bool isFromOnboard; + final RestoreType? restoreType; @override _ImportFromOtherWalletViewState createState() => @@ -95,6 +95,7 @@ class _ImportFromOtherWalletViewState extends State { @override Widget build(BuildContext context) { final l10n = context.l10n; + final isFromOnboarding = widget.restoreType != null; return BlocConsumer( listener: (context, state) async { @@ -110,14 +111,24 @@ class _ImportFromOtherWalletViewState extends State { stateMessage: state.message!, ); } + if (state.status == AppStatus.success) { /// Removes every stack except first route (splashPage) - if (widget.isFromOnboard) { - await Navigator.pushAndRemoveUntil( - context, - WalletReadyPage.route(), - (Route route) => route.isFirst, - ); + if (isFromOnboarding) { + switch (widget.restoreType!) { + case RestoreType.cryptoWallet: + await Navigator.pushAndRemoveUntil( + context, + WalletReadyPage.route(), + (Route route) => route.isFirst, + ); + case RestoreType.appBackup: + await Navigator.pushAndRemoveUntil( + context, + RestoreCredentialPage.route(fromOnBoarding: true), + (Route route) => route.isFirst, + ); + } } else { await Navigator.pushAndRemoveUntil( context, @@ -242,7 +253,7 @@ class _ImportFromOtherWalletViewState extends State { ? null : () async { await context.read().import( - isFromOnboarding: widget.isFromOnboard, + restoreType: widget.restoreType, accountName: widget.accountName, mnemonicOrKey: mnemonicController.text, ); diff --git a/lib/import_wallet/view/import_wallet_page.dart b/lib/import_wallet/view/import_wallet_page.dart index a8d5e5343..d67372f6a 100644 --- a/lib/import_wallet/view/import_wallet_page.dart +++ b/lib/import_wallet/view/import_wallet_page.dart @@ -18,23 +18,23 @@ class ImportWalletPage extends StatelessWidget { const ImportWalletPage({ super.key, this.accountName, - required this.isFromOnboarding, + required this.restoreType, }); static Route route({ String? accountName, - required bool isFromOnboarding, + required RestoreType? restoreType, }) => MaterialPageRoute( builder: (context) => ImportWalletPage( accountName: accountName, - isFromOnboarding: isFromOnboarding, + restoreType: restoreType, ), settings: const RouteSettings(name: '/ImportWalletPage'), ); final String? accountName; - final bool isFromOnboarding; + final RestoreType? restoreType; @override Widget build(BuildContext context) { @@ -50,7 +50,7 @@ class ImportWalletPage extends StatelessWidget { ), child: ImportWalletView( accountName: accountName, - isFromOnboarding: isFromOnboarding, + restoreType: restoreType, ), ); } @@ -60,11 +60,11 @@ class ImportWalletView extends StatefulWidget { const ImportWalletView({ super.key, this.accountName, - required this.isFromOnboarding, + required this.restoreType, }); final String? accountName; - final bool isFromOnboarding; + final RestoreType? restoreType; @override _ImportWalletViewState createState() => _ImportWalletViewState(); @@ -95,6 +95,8 @@ class _ImportWalletViewState extends State { Widget build(BuildContext context) { final l10n = context.l10n; + final isFromOnboarding = widget.restoreType != null; + return BlocConsumer( listener: (context, state) async { if (state.status == AppStatus.loading) { @@ -113,14 +115,24 @@ class _ImportWalletViewState extends State { stateMessage: state.message!, ); } + if (state.status == AppStatus.success) { /// Removes every stack except first route (splashPage) - if (widget.isFromOnboarding) { - await Navigator.pushAndRemoveUntil( - context, - WalletReadyPage.route(), - (Route route) => route.isFirst, - ); + if (isFromOnboarding) { + switch (widget.restoreType!) { + case RestoreType.cryptoWallet: + await Navigator.pushAndRemoveUntil( + context, + WalletReadyPage.route(), + (Route route) => route.isFirst, + ); + case RestoreType.appBackup: + await Navigator.pushAndRemoveUntil( + context, + RestoreCredentialPage.route(fromOnBoarding: true), + (Route route) => route.isFirst, + ); + } } else { await Navigator.pushAndRemoveUntil( context, @@ -145,7 +157,7 @@ class _ImportWalletViewState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ - if (widget.isFromOnboarding) + if (isFromOnboarding) const MStepper( step: 3, totalStep: 3, @@ -165,7 +177,7 @@ class _ImportWalletViewState extends State { ), ), ), - if (widget.isFromOnboarding) + if (isFromOnboarding) Column( children: [ const SizedBox(height: Sizes.spaceLarge), @@ -226,7 +238,7 @@ class _ImportWalletViewState extends State { ImportFromWalletPage.route( walletTypeModel: wallet, accountName: widget.accountName, - isFromOnboard: widget.isFromOnboarding, + restoreType: widget.restoreType, ), ); }, @@ -252,7 +264,7 @@ class _ImportWalletViewState extends State { ], ), ), - navigation: widget.isFromOnboarding + navigation: isFromOnboarding ? SafeArea( child: Padding( padding: const EdgeInsets.all(Sizes.spaceSmall), @@ -264,7 +276,7 @@ class _ImportWalletViewState extends State { await context.read().import( mnemonicOrKey: mnemonicController.text, accountName: widget.accountName, - isFromOnboarding: widget.isFromOnboarding, + restoreType: widget.restoreType, ); }, ), diff --git a/lib/l10n/arb/app_ca.arb b/lib/l10n/arb/app_ca.arb index d30e49bb9..02e58bbb0 100644 --- a/lib/l10n/arb/app_ca.arb +++ b/lib/l10n/arb/app_ca.arb @@ -81,7 +81,6 @@ "credentialPresentConfirm": "Selecciona credencial/s", "credentialPresentCancel": "Refusar", "credentialPickPresent": "Presentar", - "credentialPickTitle": "Selecciona credencial/s", "selectYourTezosAssociatedWallet": "Selecciona la teva cartera Tezos associada", "credentialPickSelect": "Selecciona la teva credencial", "siopV2credentialPickSelect": "Tria una sola credencial de la teva cartera per presentar-la", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 16a3ad7f9..5a6178b5c 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -81,7 +81,6 @@ "credentialPresentConfirm": "Select credential(s)", "credentialPresentCancel": "Reject", "credentialPickPresent": "Present", - "credentialPickTitle": "Select credential(s)", "selectYourTezosAssociatedWallet": "Select your Tezos associated wallet", "credentialPickSelect": "Select your credential", "siopV2credentialPickSelect": "Choose only one credential from your wallet to present", @@ -769,7 +768,7 @@ "keyDecentralizedIdEdSA": "did:key EdDSA", "keyDecentralizedIDSecp256k1": "did:key Secp256k1", "polygonIdDecentralizedId": "Polygon Decentralized ID", - "ebsiV3DecentralizedId": "did:key EBSI V3 P-256", + "ebsiV3DecentralizedId": "did:key EBSI P-256", "requiredCredentialNotFoundTitle": "We are unable to find the credential\nyou need in your wallet.", "requiredCredentialNotFoundSubTitle": "The required credential is not in your wallet", "requiredCredentialNotFoundDescription": "Please contact us on :", @@ -1076,15 +1075,6 @@ "keyBindingPayload": "Key Binding Payload", "ebsiV4DecentralizedId": "did:key EBSI V4 P-256", "noNotificationsYet": "No notifications yet", - "approveProfileTitle": "Install configuration", - "approveProfileDescription": "Do you consent to install the configuration of {company}?", - "@approveProfileDescription": { - "description": "name of the company owning the configuration", - "type": "text", - "placeholders": { - "company": {} - } - }, "activityLog": "Activity Log", "activityLogDescription": "See your activities", "walletInitialized": "Wallet Initialized", @@ -1121,5 +1111,47 @@ "domain": {} } }, - "keysImported": "Keys imported" + "keysImported": "Keys imported", + "approveProfileTitle": "Install configuration", + "approveProfileDescription": "Do you consent to install the configuration of {company}?", + "@approveProfileDescription": { + "description": "name of the company owning the configuration", + "type": "text", + "placeholders": { + "company": {} + } + }, + "updateProfileTitle": "Update configuration", + "updateProfileDescription": "Do you consent to update the configuration of {company}?", + "@updateProfileDescription": { + "description": "name of the company owning the configuration", + "type": "text", + "placeholders": { + "company": {} + } + }, + "replaceProfileTitle": "Install a new configuration", + "replaceProfileDescription": "Do you consent to replace the current configuration with that of {company}?", + "@replaceProfileDescription": { + "description": "name of the company owning the configuration", + "type": "text", + "placeholders": { + "company": {} + } + }, + "saveBackupCredentialSubtitle2": "To recover all your credentials you will need this backup file.", + "createWallet": "Create Wallet", + "restoreWallet": "Restore Wallet", + "showWalletRecoveryPhraseSubtitle2": "This recovery phrase is requested to restore a wallet at installation.", + "documentation": "Documentation", + "credentialPickTitle": "Choose the credential(s) you wish to obtain", + "restoreACryptoWallet": "Restore a crypto wallet", + "restoreAnAppBackup": "Restore an {appName} backup", + "@restoreAnAppBackup": { + "description": "name of the app", + "type": "text", + "placeholders": { + "appName": {} + } + } } diff --git a/lib/l10n/arb/app_es.arb b/lib/l10n/arb/app_es.arb index 6e425e024..b01fb7b2f 100644 --- a/lib/l10n/arb/app_es.arb +++ b/lib/l10n/arb/app_es.arb @@ -81,7 +81,6 @@ "credentialPresentConfirm": "Seleccionar credencial(es)", "credentialPresentCancel": "Rechazar", "credentialPickPresent": "Presentar", - "credentialPickTitle": "Seleccionar credencial(es)", "selectYourTezosAssociatedWallet": "Seleccione su cartera asociada a Tezos", "credentialPickSelect": "Seleccione su credencial", "siopV2credentialPickSelect": "Elija solo una credencial que presentar de su cartera", diff --git a/lib/l10n/arb/app_fr.arb b/lib/l10n/arb/app_fr.arb index 6c3bb36e2..6fcd1217e 100644 --- a/lib/l10n/arb/app_fr.arb +++ b/lib/l10n/arb/app_fr.arb @@ -81,7 +81,6 @@ "credentialPresentConfirm": "Sélectionnez les documents", "credentialPresentCancel": "Rejeter", "credentialPickPresent": "Présente", - "credentialPickTitle": "Sélectionnez les documents", "selectYourTezosAssociatedWallet": "Sélectionnez votre portefeuille associé Tezos", "credentialPickSelect": "Sélectionnez votre identifiant", "siopV2credentialPickSelect": "Choisissez un seul document à présenter", diff --git a/lib/l10n/untranslated.json b/lib/l10n/untranslated.json index 6fc0fbf6e..3b11f2dd5 100644 --- a/lib/l10n/untranslated.json +++ b/lib/l10n/untranslated.json @@ -46,8 +46,6 @@ "keyBindingPayload", "ebsiV4DecentralizedId", "noNotificationsYet", - "approveProfileTitle", - "approveProfileDescription", "activityLog", "activityLogDescription", "walletInitialized", @@ -56,7 +54,21 @@ "addedCredential", "deletedCredential", "presentedCredential", - "keysImported" + "keysImported", + "approveProfileTitle", + "approveProfileDescription", + "updateProfileTitle", + "updateProfileDescription", + "replaceProfileTitle", + "replaceProfileDescription", + "saveBackupCredentialSubtitle2", + "createWallet", + "restoreWallet", + "showWalletRecoveryPhraseSubtitle2", + "documentation", + "credentialPickTitle", + "restoreACryptoWallet", + "restoreAnAppBackup" ], "es": [ @@ -106,8 +118,6 @@ "keyBindingPayload", "ebsiV4DecentralizedId", "noNotificationsYet", - "approveProfileTitle", - "approveProfileDescription", "activityLog", "activityLogDescription", "walletInitialized", @@ -116,7 +126,21 @@ "addedCredential", "deletedCredential", "presentedCredential", - "keysImported" + "keysImported", + "approveProfileTitle", + "approveProfileDescription", + "updateProfileTitle", + "updateProfileDescription", + "replaceProfileTitle", + "replaceProfileDescription", + "saveBackupCredentialSubtitle2", + "createWallet", + "restoreWallet", + "showWalletRecoveryPhraseSubtitle2", + "documentation", + "credentialPickTitle", + "restoreACryptoWallet", + "restoreAnAppBackup" ], "fr": [ @@ -171,8 +195,6 @@ "keyBindingPayload", "ebsiV4DecentralizedId", "noNotificationsYet", - "approveProfileTitle", - "approveProfileDescription", "activityLog", "activityLogDescription", "walletInitialized", @@ -181,6 +203,20 @@ "addedCredential", "deletedCredential", "presentedCredential", - "keysImported" + "keysImported", + "approveProfileTitle", + "approveProfileDescription", + "updateProfileTitle", + "updateProfileDescription", + "replaceProfileTitle", + "replaceProfileDescription", + "saveBackupCredentialSubtitle2", + "createWallet", + "restoreWallet", + "showWalletRecoveryPhraseSubtitle2", + "documentation", + "credentialPickTitle", + "restoreACryptoWallet", + "restoreAnAppBackup" ] } diff --git a/lib/oidc4vc/get_authorization_uri_for_issuer.dart b/lib/oidc4vc/get_authorization_uri_for_issuer.dart index 53cc33ccf..32465ad82 100644 --- a/lib/oidc4vc/get_authorization_uri_for_issuer.dart +++ b/lib/oidc4vc/get_authorization_uri_for_issuer.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:altme/app/app.dart'; +import 'package:altme/app/shared/shared.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:did_kit/did_kit.dart'; @@ -26,6 +27,8 @@ Future getAuthorizationUriForIssuer({ String? oAuthClientAttestationPop, required bool secureAuthorizedFlow, required DioClient client, + required ProfileType profileType, + required String walletIssuer, }) async { /// this is first phase flow for authorization_code @@ -103,6 +106,9 @@ Future getAuthorizationUriForIssuer({ secureAuthorizedFlow: secureAuthorizedFlow, credentialOfferJson: credentialOfferJson, dio: client.dio, + isEBSIProfile: + profileType == ProfileType.ebsiV3 || profileType == ProfileType.ebsiV4, + walletIssuer: walletIssuer, ); final requirePushedAuthorizationRequests = diff --git a/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart b/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart index 7cd75cf25..b8d604c23 100644 --- a/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart +++ b/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart @@ -61,6 +61,7 @@ class OnBoardingGenPhraseCubit extends Cubit { SecureStorageKeys.hasVerifiedMnemonics, 'no', ); + emit(state.success()); } catch (e, s) { log.e( diff --git a/lib/onboarding/onboarding.dart b/lib/onboarding/onboarding.dart index 51fef7362..492e388c7 100644 --- a/lib/onboarding/onboarding.dart +++ b/lib/onboarding/onboarding.dart @@ -2,6 +2,7 @@ export 'activate_biometircs/activate_biometrics.dart'; export 'gen_phrase/onboarding_gen_phrase.dart'; export 'helper_function/helper_function.dart'; export 'protect_wallet/protect_wallet.dart'; +export 'restore_options/restore_options.dart'; export 'starter/starter.dart'; export 'tos/onboarding_terms.dart'; export 'verify_phrase/onboarding_verify_phrase.dart'; diff --git a/lib/onboarding/protect_wallet/view/protect_wallet_page.dart b/lib/onboarding/protect_wallet/view/protect_wallet_page.dart index 9291d2f2c..9f389e818 100644 --- a/lib/onboarding/protect_wallet/view/protect_wallet_page.dart +++ b/lib/onboarding/protect_wallet/view/protect_wallet_page.dart @@ -29,9 +29,7 @@ class ProtectWalletPage extends StatelessWidget { WalletRouteType? routeType, }) { return MaterialPageRoute( - builder: (_) => ProtectWalletPage( - routeType: routeType, - ), + builder: (_) => ProtectWalletPage(routeType: routeType), settings: const RouteSettings(name: '/ProtectWalletPage'), ); } @@ -87,7 +85,7 @@ class _ProtectWalletViewState extends State { bool get isFromOnboarding => widget.routeType != null; - Future createImportAccount({required bool byPassScreen}) async { + Future createImportAccount() async { if (widget.routeType == WalletRouteType.create) { if (byPassScreen) { await widget.onboardingCubit.emitOnboardingProcessing(); @@ -99,11 +97,14 @@ class _ProtectWalletViewState extends State { await Navigator.of(context).push(OnBoardingGenPhrasePage.route()); } } else { - await Navigator.of(context).push( - ImportWalletPage.route( - isFromOnboarding: true, - ), - ); + /// import case + if (Parameters.importAndRestoreAtOnboarding) { + await Navigator.of(context).push(RestoreOptionsPage.route()); + } else { + await Navigator.of(context).push( + ImportWalletPage.route(restoreType: RestoreType.appBackup), + ); + } } } @@ -171,9 +172,7 @@ class _ProtectWalletViewState extends State { ); Navigator.of(context).pop(); if (isFromOnboarding) { - await createImportAccount( - byPassScreen: byPassScreen, - ); + await createImportAccount(); } else { AlertMessage.showStateMessage( context: context, @@ -208,9 +207,7 @@ class _ProtectWalletViewState extends State { ); Navigator.of(context).pop(); if (isFromOnboarding) { - await createImportAccount( - byPassScreen: byPassScreen, - ); + await createImportAccount(); } else { AlertMessage.showStateMessage( context: context, @@ -250,9 +247,7 @@ class _ProtectWalletViewState extends State { ); Navigator.of(context).pop(); if (isFromOnboarding) { - await createImportAccount( - byPassScreen: byPassScreen, - ); + await createImportAccount(); } else { AlertMessage.showStateMessage( context: context, diff --git a/lib/onboarding/restore_options/cubit/restore_options_cubit.dart b/lib/onboarding/restore_options/cubit/restore_options_cubit.dart new file mode 100644 index 000000000..2e506b8fe --- /dev/null +++ b/lib/onboarding/restore_options/cubit/restore_options_cubit.dart @@ -0,0 +1,10 @@ +import 'package:altme/app/app.dart'; +import 'package:bloc/bloc.dart'; + +class RestoreOptionsCubit extends Cubit { + RestoreOptionsCubit() : super(RestoreType.cryptoWallet); + + void updateSwitch(RestoreType restoreType) { + emit(restoreType); + } +} diff --git a/lib/onboarding/restore_options/restore_options.dart b/lib/onboarding/restore_options/restore_options.dart new file mode 100644 index 000000000..0af13c926 --- /dev/null +++ b/lib/onboarding/restore_options/restore_options.dart @@ -0,0 +1,3 @@ +export 'cubit/restore_options_cubit.dart'; +export 'view/restore_options_view.dart'; +export 'widget/widget.dart'; diff --git a/lib/onboarding/restore_options/view/restore_options_view.dart b/lib/onboarding/restore_options/view/restore_options_view.dart new file mode 100644 index 000000000..8ffbaf343 --- /dev/null +++ b/lib/onboarding/restore_options/view/restore_options_view.dart @@ -0,0 +1,82 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/import_wallet/import_wallet.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:altme/onboarding/onboarding.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class RestoreOptionsPage extends StatelessWidget { + const RestoreOptionsPage({ + super.key, + }); + + static Route route() { + return MaterialPageRoute( + builder: (_) => const RestoreOptionsPage(), + settings: const RouteSettings(name: '/RestoreOptionsPage'), + ); + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => RestoreOptionsCubit(), + child: const RestoreOptionsView(), + ); + } +} + +class RestoreOptionsView extends StatelessWidget { + const RestoreOptionsView({super.key}); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return BlocBuilder( + builder: (context, state) { + return BasePage( + scrollView: false, + useSafeArea: true, + padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceXSmall), + titleLeading: const BackLeadingButton(), + backgroundColor: Theme.of(context).colorScheme.surface, + title: l10n.restore, + titleAlignment: Alignment.topCenter, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: Sizes.spaceNormal), + RestoreOptionWidget( + title: l10n.restoreACryptoWallet, + isSelected: state == RestoreType.cryptoWallet, + onTap: () => context + .read() + .updateSwitch(RestoreType.cryptoWallet), + ), + RestoreOptionWidget( + title: l10n.restoreAnAppBackup(Parameters.appName), + isSelected: state == RestoreType.appBackup, + onTap: () => context + .read() + .updateSwitch(RestoreType.appBackup), + ), + ], + ), + navigation: Padding( + padding: const EdgeInsets.all( + Sizes.spaceSmall, + ), + child: MyElevatedButton( + text: l10n.continueString, + onPressed: () { + Navigator.of(context) + .push(ImportWalletPage.route(restoreType: state)); + }, + ), + ), + ); + }, + ); + } +} diff --git a/lib/onboarding/restore_options/widget/restore_option_widget.dart b/lib/onboarding/restore_options/widget/restore_option_widget.dart new file mode 100644 index 000000000..8484bfcaa --- /dev/null +++ b/lib/onboarding/restore_options/widget/restore_option_widget.dart @@ -0,0 +1,64 @@ +import 'package:altme/app/app.dart'; + +import 'package:flutter/material.dart'; + +class RestoreOptionWidget extends StatelessWidget { + const RestoreOptionWidget({ + super.key, + required this.title, + this.isSelected = false, + this.onTap, + }); + + final String title; + final VoidCallback? onTap; + final bool isSelected; + + @override + Widget build(BuildContext context) { + return TransparentInkWell( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: Sizes.spaceNormal, + vertical: Sizes.spaceSmall, + ), + margin: const EdgeInsets.all(Sizes.spaceXSmall), + decoration: BoxDecoration( + color: isSelected + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all( + Radius.circular( + Sizes.normalRadius, + ), + ), + ), + child: SizedBox( + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: isSelected + ? Theme.of(context).textTheme.titleLarge!.copyWith( + color: Theme.of(context).colorScheme.onPrimary, + ) + : Theme.of(context).textTheme.titleLarge, + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/onboarding/restore_options/widget/widget.dart b/lib/onboarding/restore_options/widget/widget.dart new file mode 100644 index 000000000..6ca9b662e --- /dev/null +++ b/lib/onboarding/restore_options/widget/widget.dart @@ -0,0 +1 @@ +export 'restore_option_widget.dart'; diff --git a/lib/onboarding/starter/view/starter_page.dart b/lib/onboarding/starter/view/starter_page.dart index dd45839a5..5a5d0add6 100644 --- a/lib/onboarding/starter/view/starter_page.dart +++ b/lib/onboarding/starter/view/starter_page.dart @@ -71,7 +71,7 @@ class StarterView extends StatelessWidget { SubTitle(profileModel: state.model), const Spacer(flex: 4), MyOutlinedButton( - text: l10n.importAccount, + text: l10n.restoreWallet, onPressed: () async { await profileCubit.setWalletType( walletType: WalletType.personal, @@ -89,7 +89,7 @@ class StarterView extends StatelessWidget { ), const SizedBox(height: 10), MyElevatedButton( - text: l10n.createAccount, + text: l10n.createWallet, verticalSpacing: 15, onPressed: () async { await profileCubit.setWalletType( diff --git a/lib/pin_code/widgets/pin_code_widget.dart b/lib/pin_code/widgets/pin_code_widget.dart index f981636f7..7228e8a78 100644 --- a/lib/pin_code/widgets/pin_code_widget.dart +++ b/lib/pin_code/widgets/pin_code_widget.dart @@ -135,6 +135,7 @@ class _PinCodeWidgetState extends State children: [ Stack( children: [ + if (widget.showKeyboard) const BackLeadingButton(), Column( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -149,6 +150,8 @@ class _PinCodeWidgetState extends State 0.5, showPoweredBy: true, ), + if (widget.showKeyboard) + const SizedBox(height: 80), const SizedBox(height: Sizes.spaceSmall), PinCodeTitle( title: widget.title, @@ -207,6 +210,8 @@ class _PinCodeWidgetState extends State decoration: const InputDecoration( border: InputBorder.none, ), + onEditingComplete: () {}, + onSubmitted: (value) {}, ), ), ], @@ -293,6 +298,8 @@ class _PinCodeWidgetState extends State decoration: const InputDecoration( border: InputBorder.none, ), + onEditingComplete: () {}, + onSubmitted: (value) {}, ), ), ], diff --git a/lib/scan/cubit/scan_cubit.dart b/lib/scan/cubit/scan_cubit.dart index ec1c6e509..aa8dc46fa 100644 --- a/lib/scan/cubit/scan_cubit.dart +++ b/lib/scan/cubit/scan_cubit.dart @@ -524,7 +524,18 @@ class ScanCubit extends Cubit { if (presentationDefinition.format == null) { clientMetaData = await getClientMetada(client: client, uri: uri); } + // final String vpFormat = getVpFormat( + // presentationDefinition: presentationDefinition, + // clientMetaData: clientMetaData, + // ); + final (presentationSubmission, formatFromPresentationSubmission) = + await getPresentationSubmission( + credentialsToBePresented: credentialsToBePresented, + presentationDefinition: presentationDefinition, + clientMetaData: clientMetaData, + profileSetting: qrCodeScanCubit.profileCubit.state.model.profileSetting, + ); final String vpToken = await createVpToken( credentialsToBePresented: credentialsToBePresented, did: did, @@ -535,13 +546,7 @@ class ScanCubit extends Cubit { uri: uri, clientMetaData: clientMetaData, profileSetting: qrCodeScanCubit.profileCubit.state.model.profileSetting, - ); - - final presentationSubmission = await getPresentationSubmission( - credentialsToBePresented: credentialsToBePresented, - presentationDefinition: presentationDefinition, - clientMetaData: clientMetaData, - profileSetting: qrCodeScanCubit.profileCubit.state.model.profileSetting, + formatFromPresentationSubmission: formatFromPresentationSubmission, ); Map body; @@ -701,7 +706,7 @@ class ScanCubit extends Cubit { } } - Future> getPresentationSubmission({ + Future<(Map, VCFormatType)> getPresentationSubmission({ required List credentialsToBePresented, required PresentationDefinition presentationDefinition, required Map? clientMetaData, @@ -714,70 +719,11 @@ class ScanCubit extends Cubit { 'definition_id': presentationDefinition.id, }; - final vcFormatType = profileSetting - .selfSovereignIdentityOptions.customOidc4vcProfile.vcFormatType; + // final vcFormatType = profileSetting + // .selfSovereignIdentityOptions.customOidc4vcProfile.vcFormatType; final inputDescriptors = >[]; - - String? vcFormat; - String? vpFormat; - - if (presentationDefinition.format != null) { - final ldpVc = presentationDefinition.format?.ldpVc != null; - final jwtVc = presentationDefinition.format?.jwtVc != null; - final jwtVcJson = presentationDefinition.format?.jwtVcJson != null; - - if (ldpVc) { - vcFormat = 'ldp_vc'; - } else if (jwtVc) { - vcFormat = 'jwt_vc'; - } else if (jwtVcJson) { - vcFormat = 'jwt_vc_json'; - } - - final ldpVp = presentationDefinition.format?.ldpVp != null; - final jwtVp = presentationDefinition.format?.jwtVp != null; - final jwtVpJson = presentationDefinition.format?.jwtVpJson != null; - final vcSdJwt = presentationDefinition.format?.vcSdJwt != null; - - if (ldpVp) { - vpFormat = 'ldp_vp'; - } else if (jwtVp) { - vpFormat = 'jwt_vp'; - } else if (jwtVpJson) { - vpFormat = 'jwt_vp_json'; - } else if (vcSdJwt) { - vpFormat = 'vc+sd-jwt'; - vcFormat = 'vc+sd-jwt'; - } - } else { - if (clientMetaData == null) { - vcFormat = vcFormatType.vcValue; - vpFormat = vcFormatType.vpValue; - } else { - final vpFormats = clientMetaData['vp_formats'] as Map; - - if (vpFormats.containsKey('ldp_vc')) { - vcFormat = 'ldp_vc'; - } else if (vpFormats.containsKey('jwt_vc')) { - vcFormat = 'jwt_vc'; - } else if (vpFormats.containsKey('jwt_vc_json')) { - vcFormat = 'jwt_vc_json'; - } - - if (vpFormats.containsKey('ldp_vp')) { - vpFormat = 'ldp_vp'; - } else if (vpFormats.containsKey('jwt_vp')) { - vpFormat = 'jwt_vp'; - } else if (vpFormats.containsKey('jwt_vp_json')) { - vpFormat = 'jwt_vp_json'; - } else if (vpFormats.containsKey('vc+sd-jwt')) { - vpFormat = 'vc+sd-jwt'; - vcFormat = 'vc+sd-jwt'; - } - } - } - + VCFormatType formatFromPresentationSubmission = VCFormatType.vcSdJWT; for (int i = 0; i < credentialsToBePresented.length; i++) { for (final InputDescriptor inputDescriptor in presentationDefinition.inputDescriptors) { @@ -787,38 +733,35 @@ class ScanCubit extends Cubit { filterList: filterList, credentialList: [credentialsToBePresented[i]], ); - + final format = getVcFormatType(credential[0].getFormat); Map? pathNested; - if (vcFormatType != VCFormatType.vcSdJWT) { - pathNested = { - 'id': inputDescriptor.id, - 'format': vcFormat, - }; - } - if (credential.isNotEmpty) { final Map descriptor = { 'id': inputDescriptor.id, - 'format': vpFormat, + 'format': format.vpValue, 'path': r'$', }; - if (vcFormatType != VCFormatType.vcSdJWT && pathNested != null) { + if (format != VCFormatType.vcSdJWT) { + pathNested = { + 'id': inputDescriptor.id, + 'format': format.vpValue, + }; if (credentialsToBePresented.length == 1) { - if (vpFormat == 'ldp_vp') { + if (format == VCFormatType.ldpVc) { pathNested['path'] = r'$.verifiableCredential'; - } else if (vpFormat == 'vc+sd-jwt') { + } else if (format == VCFormatType.vcSdJWT) { pathNested['path'] = r'$'; } else { pathNested['path'] = r'$.vp.verifiableCredential[0]'; } } else { - if (vpFormat == 'ldp_vp') { + if (format == VCFormatType.ldpVc) { pathNested['path'] = // ignore: prefer_interpolation_to_compose_strings r'$.verifiableCredential[' + i.toString() + ']'; - } else if (vpFormat == 'vc+sd-jwt') { + } else if (format == VCFormatType.vcSdJWT) { pathNested['path'] = r'$'; } else { pathNested['path'] = @@ -826,18 +769,19 @@ class ScanCubit extends Cubit { r'$.vp.verifiableCredential[' + i.toString() + ']'; } } - pathNested['format'] = vcFormat ?? vcFormatType.vcValue; + pathNested['format'] = format.vcValue; descriptor['path_nested'] = pathNested; } inputDescriptors.add(descriptor); + formatFromPresentationSubmission = format; } } } presentationSubmission['descriptor_map'] = inputDescriptors; - return presentationSubmission; + return (presentationSubmission, formatFromPresentationSubmission); } Future askPermissionDIDAuthCHAPI({ @@ -868,6 +812,7 @@ class ScanCubit extends Cubit { required Uri uri, required Map? clientMetaData, required ProfileSetting profileSetting, + VCFormatType? formatFromPresentationSubmission, }) async { final nonce = uri.queryParameters['nonce'] ?? ''; final clientId = uri.queryParameters['client_id'] ?? ''; @@ -877,15 +822,48 @@ class ScanCubit extends Cubit { final vcFormatType = customOidc4vcProfile.vcFormatType; - final (presentLdpVc, presentJwtVc, presentJwtVcJson, presentVcSdJwt) = - getPresentVCDetails( + final supportingFormats = getPresentVCDetails( clientMetaData: clientMetaData, presentationDefinition: presentationDefinition, vcFormatType: vcFormatType, credentialsToBePresented: credentialsToBePresented, ); - if (presentLdpVc) { + // if (supportingFormats.contains(VCFormatType.vcSdJWT)) { + if (formatFromPresentationSubmission == VCFormatType.vcSdJWT) { + final credentialList = getStringCredentialsForToken( + credentialsToBePresented: credentialsToBePresented, + profileCubit: profileCubit, + ); + + final vpToken = credentialList.first; + // considering only one + + return vpToken; + // } else if (supportingFormats.contains(VCFormatType.jwtVc) || + // supportingFormats.contains(VCFormatType.jwtVcJson) || + // supportingFormats.contains(VCFormatType.jwtVcJsonLd)) { + } else if (formatFromPresentationSubmission == VCFormatType.jwtVc || + formatFromPresentationSubmission == VCFormatType.jwtVcJson || + formatFromPresentationSubmission == VCFormatType.jwtVcJsonLd) { + final credentialList = getStringCredentialsForToken( + credentialsToBePresented: credentialsToBePresented, + profileCubit: profileCubit, + ); + + final vpToken = await oidc4vc.extractVpToken( + clientId: clientId, + credentialsToBePresented: credentialList, + did: did, + kid: kid, + privateKey: privateKey, + nonce: nonce, + proofHeaderType: customOidc4vcProfile.proofHeader, + ); + + return vpToken; + // } else if (supportingFormats.contains(VCFormatType.ldpVc)) { + } else if (formatFromPresentationSubmission == VCFormatType.ldpVc) { /// proof is done with a creation date 20 seconds in the past to avoid /// proof check to fail because of time difference on server final options = jsonEncode({ @@ -913,33 +891,6 @@ class ScanCubit extends Cubit { options, privateKey, ); - return vpToken; - } else if (presentJwtVc || presentJwtVcJson) { - final credentialList = getStringCredentialsForToken( - credentialsToBePresented: credentialsToBePresented, - profileCubit: profileCubit, - ); - - final vpToken = await oidc4vc.extractVpToken( - clientId: clientId, - credentialsToBePresented: credentialList, - did: did, - kid: kid, - privateKey: privateKey, - nonce: nonce, - proofHeaderType: customOidc4vcProfile.proofHeader, - ); - - return vpToken; - } else if (presentVcSdJwt) { - final credentialList = getStringCredentialsForToken( - credentialsToBePresented: credentialsToBePresented, - profileCubit: profileCubit, - ); - - final vpToken = credentialList.first; - // considering only one - return vpToken; } else { throw ResponseMessage( @@ -1023,4 +974,11 @@ class ScanCubit extends Cubit { ); } } + + String getVpFormat({ + required PresentationDefinition presentationDefinition, + Map? clientMetaData, + }) { + return 'vc+sd-jwt'; + } } diff --git a/lib/selective_disclosure/helper_functions/selective_disclosure_display_map.dart b/lib/selective_disclosure/helper_functions/selective_disclosure_display_map.dart index 88cafed82..7f64139bb 100644 --- a/lib/selective_disclosure/helper_functions/selective_disclosure_display_map.dart +++ b/lib/selective_disclosure/helper_functions/selective_disclosure_display_map.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:altme/app/shared/constants/parameters.dart'; import 'package:altme/app/shared/extension/iterable_extension.dart'; +import 'package:altme/app/shared/helper_functions/get_display.dart'; import 'package:altme/dashboard/home/tab_bar/credentials/models/credential_model/credential_model.dart'; import 'package:altme/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; @@ -47,7 +48,7 @@ class SelectiveDisclosureDisplayMap { if (mapValue is! Map) return; - final display = getDisplay(mapKey, mapValue, languageCode); + final display = getDisplay(mapValue, languageCode); title = display?['name'].toString() ?? '${Parameters.doNotDisplayMe}$index'; @@ -290,7 +291,15 @@ class SelectiveDisclosureDisplayMap { .indexWhere( (entry) => entry.value.toString().contains(claimKey), ); - if (indexInDisclosure == -1) isDisabled = true; + if (indexInDisclosure == -1) { + isDisabled = true; + } else if (isDisabled) { + // Don't add in the map if limitDisclosure is required and the + // considered element is: + // - From the SD + // - Not targeted by the filters + continue; + } // ignore: inference_failure_on_uninitialized_variable, prefer_typing_uninitialized_variables late final value; try { @@ -324,41 +333,6 @@ class SelectiveDisclosureDisplayMap { return claimDataMap; } - dynamic getDisplay(String key, dynamic value, String languageCode) { - if (value is! Map) return null; - - if (value.isEmpty) return null; - - if (value.containsKey('display')) { - final displays = value['display']; - if (displays is! List) return null; - if (displays.isEmpty) return null; - - final display = displays.firstWhere( - (element) => - element is Map && - element.containsKey('locale') && - element['locale'].toString().contains(languageCode), - orElse: () => displays.firstWhere( - (element) => - element is Map && - element.containsKey('locale') && - element['locale'].toString().contains('en'), - orElse: () => displays.firstWhere( - (element) => - element is Map && - element.containsKey('locale'), - orElse: () => null, - ), - ), - ); - - return display; - } else { - return null; - } - } - bool isNestedInpayload(List contents, String digest) { final payloadSd = SelectiveDisclosure(credentialModel).payload; final JsonPath dataPathSd = JsonPath( diff --git a/lib/selective_disclosure/selective_disclosure.dart b/lib/selective_disclosure/selective_disclosure.dart index 02b2ca82d..d49865d8f 100644 --- a/lib/selective_disclosure/selective_disclosure.dart +++ b/lib/selective_disclosure/selective_disclosure.dart @@ -222,7 +222,7 @@ class SelectiveDisclosure { value.add( ClaimsData( isfromDisclosureOfJWT: false, - data: data.toString(), + data: data is Map ? jsonEncode(data) : data.toString(), ), ); } catch (e) { diff --git a/lib/selective_disclosure/widget/display_selective_disclosure.dart b/lib/selective_disclosure/widget/display_selective_disclosure.dart index 018687b1a..2ce2c8e92 100644 --- a/lib/selective_disclosure/widget/display_selective_disclosure.dart +++ b/lib/selective_disclosure/widget/display_selective_disclosure.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:altme/app/shared/constants/parameters.dart'; import 'package:altme/app/shared/extension/iterable_extension.dart'; import 'package:altme/app/shared/widget/base/credential_field.dart'; @@ -290,6 +292,13 @@ class DisclosureLine extends StatelessWidget { if (title != null) { title = title.startsWith(Parameters.doNotDisplayMe) ? null : title; } + late String value; + + if (elementValue['value'] is Map) { + value = jsonEncode(elementValue['value']); + } else { + value = elementValue['value'].toString(); + } return TransparentInkWell( onTap: () { if (elementValue['hasCheckbox'] != true || @@ -310,7 +319,7 @@ class DisclosureLine extends StatelessWidget { child: CredentialField( padding: const EdgeInsets.only(top: 8), title: title, - value: elementValue['value'].toString(), + value: value, titleColor: Theme.of(context).colorScheme.onSurface, valueColor: Theme.of(context).colorScheme.onSurface, showVertically: showVertically, diff --git a/lib/splash/bloclisteners/blocklisteners.dart b/lib/splash/bloclisteners/blocklisteners.dart index 14a578e61..9bc58a837 100644 --- a/lib/splash/bloclisteners/blocklisteners.dart +++ b/lib/splash/bloclisteners/blocklisteners.dart @@ -881,20 +881,40 @@ final enterpriseBlocListener = BlocListener( ); } - if (state.status == AppStatus.walletProviderApproval) { + if (state.status == AppStatus.addEnterpriseAccount || + state.status == AppStatus.updateEnterpriseAccount || + state.status == AppStatus.replaceEnterpriseAccount) { final settingJson = state.profileSettingJson; if (settingJson != null) { final l10n = context.l10n; final profileSetting = ProfileSetting.fromJson( jsonDecode(settingJson) as Map, ); + + var title = l10n.approveProfileTitle; + var subTitle = l10n.approveProfileDescription( + profileSetting.generalOptions.companyName, + ); + + if (state.status == AppStatus.updateEnterpriseAccount) { + title = l10n.updateProfileTitle; + subTitle = l10n.updateProfileDescription( + profileSetting.generalOptions.companyName, + ); + } + + if (state.status == AppStatus.replaceEnterpriseAccount) { + title = l10n.replaceProfileTitle; + subTitle = l10n.replaceProfileDescription( + profileSetting.generalOptions.companyName, + ); + } + final confirm = await showDialog( context: context, builder: (_) => ConfirmDialog( - title: l10n.approveProfileTitle, - subtitle: l10n.approveProfileDescription( - profileSetting.generalOptions.companyName, - ), + title: title, + subtitle: subTitle, yes: l10n.showDialogYes, no: l10n.showDialogNo, ), @@ -902,9 +922,10 @@ final enterpriseBlocListener = BlocListener( false; if (confirm) { - await context - .read() - .applyConfiguration(context.read()); + await context.read().applyConfiguration( + qrCodeScanCubit: context.read(), + status: state.status, + ); } else { /// Need to remove the enterprise email from secure storage /// because we may think later that the entreprise profile is diff --git a/packages/oidc4vc/lib/src/oidc4vc.dart b/packages/oidc4vc/lib/src/oidc4vc.dart index 54e922108..2b6f3083c 100644 --- a/packages/oidc4vc/lib/src/oidc4vc.dart +++ b/packages/oidc4vc/lib/src/oidc4vc.dart @@ -149,6 +149,8 @@ class OIDC4VC { required bool secureAuthorizedFlow, required Dio dio, required dynamic credentialOfferJson, + required bool isEBSIProfile, + required String walletIssuer, SecureStorageProvider? secureStorage, String? oAuthClientAttestation, String? oAuthClientAttestationPop, @@ -187,6 +189,8 @@ class OIDC4VC { oidc4vciDraftType: oidc4vciDraftType, vcFormatType: vcFormatType, secureAuthorizedFlow: secureAuthorizedFlow, + isEBSIProfile: isEBSIProfile, + walletIssuer: walletIssuer, ); return ( @@ -217,6 +221,8 @@ class OIDC4VC { required OIDC4VCIDraftType oidc4vciDraftType, required VCFormatType vcFormatType, required bool secureAuthorizedFlow, + required bool isEBSIProfile, + required String walletIssuer, }) { //https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-successful-authorization-re @@ -339,13 +345,18 @@ class OIDC4VC { myRequest['issuer_state'] = issuerState; } - if (secureAuthorizedFlow) { - myRequest['client_metadata'] = - Uri.encodeComponent(jsonEncode(clientMetaData)); - } else if (clientAuthentication != ClientAuthentication.clientSecretJwt) { - myRequest['client_metadata'] = jsonEncode(clientMetaData); - // paramètre config du portail, on ne met pas si : client authentication : + if (isEBSIProfile) { + if (secureAuthorizedFlow) { + myRequest['client_metadata'] = + Uri.encodeComponent(jsonEncode(clientMetaData)); + } else if (clientAuthentication != ClientAuthentication.clientSecretJwt) { + myRequest['client_metadata'] = jsonEncode(clientMetaData); + // paramètre config du portail, on ne met pas si : client authentication : + } + } else { + myRequest['wallet_issuer'] = walletIssuer; } + switch (clientAuthentication) { case ClientAuthentication.none: break; @@ -1103,7 +1114,6 @@ class OIDC4VC { tokenParameters: issuerTokenParameters, clientAuthentication: clientAuthentication, cnonce: nonce, - iss: issuerTokenParameters.clientId, ); credentialData['proof'] = { @@ -1400,13 +1410,11 @@ class OIDC4VC { Future getIssuerJwt({ required IssuerTokenParameters tokenParameters, required ClientAuthentication clientAuthentication, - required String iss, String? cnonce, }) async { final iat = (DateTime.now().millisecondsSinceEpoch / 1000).round() - 30; final payload = { - 'iss': iss, 'iat': iat, 'aud': tokenParameters.issuer, }; @@ -1592,7 +1600,7 @@ class OIDC4VC { 'aud': tokenParameters.audience, 'exp': iat + 1000, 'sub': tokenParameters.did, - 'iss': tokenParameters.did, + //'iss': tokenParameters.did, 'vp': { '@context': ['https://www.w3.org/2018/credentials/v1'], 'id': presentationId, @@ -1697,7 +1705,7 @@ class OIDC4VC { 'aud': tokenParameters.audience, // devrait être verifier 'exp': iat + 1000, 'sub': issAndSub, - 'iss': issAndSub, + // 'iss': issAndSub, }; if (tokenParameters.nonce != null) { diff --git a/packages/oidc4vc/lib/src/vc_format_type.dart b/packages/oidc4vc/lib/src/vc_format_type.dart index 210ea3fc3..f9675b18f 100644 --- a/packages/oidc4vc/lib/src/vc_format_type.dart +++ b/packages/oidc4vc/lib/src/vc_format_type.dart @@ -80,3 +80,12 @@ extension VCFormatTypeX on VCFormatType { } } } + +VCFormatType getVcFormatType(String formatString) { + for (final element in VCFormatType.values) { + if (element.vcValue == formatString) { + return element; + } + } + throw Exception('Invalid VCFormatType'); +} diff --git a/packages/oidc4vc/test/src/oidc4vc_test.dart b/packages/oidc4vc/test/src/oidc4vc_test.dart index 925d19fce..839224a1e 100644 --- a/packages/oidc4vc/test/src/oidc4vc_test.dart +++ b/packages/oidc4vc/test/src/oidc4vc_test.dart @@ -277,6 +277,8 @@ void main() { dio: client, credentialOfferJson: credentialOfferJson, secureStorage: mockSecureStorage, + isEBSIProfile: true, + walletIssuer: 'https://app.talao.co/wallet_issuer', ); expect(authorizationEndpoint, expectedAuthorizationEndpoint); @@ -312,6 +314,8 @@ void main() { vcFormatType: VCFormatType.jwtVc, credentialOfferJson: credentialOfferJson, dio: client, + isEBSIProfile: true, + walletIssuer: 'https://app.talao.co/wallet_issuer', ), throwsA( isA().having( @@ -350,6 +354,8 @@ void main() { jsonDecode(openIdConfiguration) as Map, ), redirectUri: redirectUri, + isEBSIProfile: true, + walletIssuer: 'https://app.talao.co/wallet_issuer', ); expect( @@ -434,6 +440,8 @@ void main() { dio: client, credentialOfferJson: credentialOfferJsonAuthorizedTest10, secureStorage: mockSecureStorage, + isEBSIProfile: true, + walletIssuer: 'https://app.talao.co/wallet_issuer', ); expect(authorizationEndpoint, expectedAuthorizationEndpoint); @@ -496,6 +504,8 @@ void main() { dio: client, credentialOfferJson: credentialOfferJsonPreAuthorizedTest10, secureStorage: mockSecureStorage, + isEBSIProfile: true, + walletIssuer: 'https://app.talao.co/wallet_issuer', ); expect(authorizationEndpoint, expectedAuthorizationEndpoint); @@ -674,7 +684,6 @@ void main() { final issuerJwt = await oidc4vc.getIssuerJwt( tokenParameters: tokenParameters, clientAuthentication: ClientAuthentication.clientId, - iss: clientId, cnonce: '2da2d506-0910-11ef-9e49-0a1628958560', ); diff --git a/pubspec.lock b/pubspec.lock index f79a30693..5b17f33a8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: android_intent_plus - sha256: "007703c1b2cac7ca98add3336b98cffa4baa11d5133cc463293dba9daa39cdf6" + sha256: "38921ec22ebb3b9a7eb678792cf6fab0b6f458b61b9d327688573449c9b47db3" url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.2.0" ansicolor: dependency: transitive description: @@ -446,18 +446,18 @@ packages: dependency: "direct main" description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" coverage: dependency: transitive description: name: coverage - sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 + sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" url: "https://pub.dev" source: hosted - version: "1.9.2" + version: "1.10.0" credential_manifest: dependency: "direct main" description: @@ -477,10 +477,10 @@ packages: dependency: "direct main" description: name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" crypto_keys: dependency: transitive description: @@ -859,10 +859,10 @@ packages: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -1271,10 +1271,10 @@ packages: dependency: "direct main" description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.3.0" image_picker: dependency: "direct main" description: @@ -1295,10 +1295,10 @@ packages: dependency: transitive description: name: image_picker_for_web - sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50" + sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" image_picker_ios: dependency: transitive description: @@ -1525,10 +1525,10 @@ packages: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" markdown: dependency: "direct main" description: @@ -1820,10 +1820,10 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa" + sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1" url: "https://pub.dev" source: hosted - version: "12.0.12" + version: "12.0.13" permission_handler_apple: dependency: transitive description: @@ -1884,10 +1884,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" platform_device_id: dependency: "direct main" description: @@ -2505,10 +2505,10 @@ packages: dependency: transitive description: name: url_launcher_windows - sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" uuid: dependency: "direct overridden" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4a8d00bcb..92f200ebc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,7 @@ name: altme description: AltMe Flutter App - -version: 2.15.3+529 +version: 2.15.6+532 publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: