diff --git a/lib/app/shared/constants/secure_storage_keys.dart b/lib/app/shared/constants/secure_storage_keys.dart index 2655f0efc..a4d602f8f 100644 --- a/lib/app/shared/constants/secure_storage_keys.dart +++ b/lib/app/shared/constants/secure_storage_keys.dart @@ -32,6 +32,8 @@ class SecureStorageKeys { static const String enable4DigitPINCode = 'enable4DigitPINCode'; static const String isDeveloperMode = 'isDeveloperMode'; static const String enableJWKThumbprint = 'enableJWKThumbprint'; + static const String enableCryptographicHolderBinding = + 'enableCryptographicHolderBinding'; static const String pinCode = 'pinCode'; static const String data = 'data'; diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index 49edf1d1f..32844e0b3 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -724,6 +724,40 @@ Future?> getPresentationDefinition({ } } +Future?> getClientMetada({ + required Uri uri, + required DioClient client, +}) async { + try { + final keys = []; + uri.queryParameters.forEach((key, value) => keys.add(key)); + + if (keys.contains('client_metadata')) { + final String clientMetaDataValue = + uri.queryParameters['client_metadata'] ?? ''; + + final json = jsonDecode(clientMetaDataValue.replaceAll("'", '"')) + as Map; + + return json; + } else if (keys.contains('client_metadata_uri')) { + final clientMetaDataUri = + uri.queryParameters['client_metadata_uri'].toString(); + final dynamic response = await client.get(clientMetaDataUri); + + final Map data = response == String + ? jsonDecode(response.toString()) as Map + : response as Map; + + return data; + } else { + return null; + } + } catch (e) { + return null; + } +} + Future isEBSIV3ForVerifiers({ required Uri uri, required DioClient client, @@ -993,39 +1027,59 @@ String getFormattedStringOIDC4VCI({ dynamic credentialOfferJson, }) { return ''' -SCHEME : ${getSchemeFromUrl(url)} -\n -CREDENTIAL OFFER : -${credentialOfferJson != null ? const JsonEncoder.withIndent(' ').convert(credentialOfferJson) : 'None'} -\n -ENDPOINTS : +SCHEME : ${getSchemeFromUrl(url)}\n +CREDENTIAL OFFER : +${credentialOfferJson != null ? const JsonEncoder.withIndent(' ').convert(credentialOfferJson) : 'None'}\n +ENDPOINTS : authorization server endpoint : ${openidConfigurationResponse?['authorization_server'] ?? 'None'} token endpoint : ${openidConfigurationResponse?['token_endpoint'] ?? authorizationServerConfiguration?['token_endpoint'] ?? 'None'} credential endpoint : ${openidConfigurationResponse?['credential_endpoint'] ?? 'None'} deferred endpoint : ${openidConfigurationResponse?['deferred_endpoint'] ?? 'None'} - batch endpoint : ${openidConfigurationResponse?['batch_endpoint'] ?? 'None'} -\n -CREDENTIAL SUPPORTED : -${openidConfigurationResponse?['credentials_supported'] != null ? const JsonEncoder.withIndent(' ').convert(openidConfigurationResponse!['credentials_supported']) : 'None'} -\n -AUTHORIZATION SERVER CONFIGURATION : -${authorizationServerConfiguration != null ? const JsonEncoder.withIndent(' ').convert(authorizationServerConfiguration) : 'None'} -\n -CRDENTIAL ISSUER CONFIGURATION : + batch endpoint : ${openidConfigurationResponse?['batch_endpoint'] ?? 'None'}\n +CREDENTIAL SUPPORTED : +${openidConfigurationResponse?['credentials_supported'] != null ? const JsonEncoder.withIndent(' ').convert(openidConfigurationResponse!['credentials_supported']) : 'None'}\n +AUTHORIZATION SERVER CONFIGURATION : +${authorizationServerConfiguration != null ? const JsonEncoder.withIndent(' ').convert(authorizationServerConfiguration) : 'None'}\n +CRDENTIAL ISSUER CONFIGURATION : ${openidConfigurationResponse != null ? const JsonEncoder.withIndent(' ').convert(openidConfigurationResponse) : 'None'} '''; } -String getFormattedStringOIDC4VPSIOPV2({ +Future getFormattedStringOIDC4VPSIOPV2({ required String url, - required Map? presentationDefinition, -}) { - return ''' -SCHEME : ${getSchemeFromUrl(url)} -\n -PRESENTATION DEFINITION : -${presentationDefinition != null ? const JsonEncoder.withIndent(' ').convert(presentationDefinition) : 'None'} + required DioClient client, + required Map? response, +}) async { + final Map? presentationDefinition = + await getPresentationDefinition( + client: client, + uri: Uri.parse(url), + ); + + final Map? clientMetaData = await getClientMetada( + client: client, + uri: Uri.parse(url), + ); + + final registration = Uri.parse(url).queryParameters['registration']; + + final registrationMap = registration != null + ? jsonDecode(registration) as Map + : null; + + final data = ''' +SCHEME : ${getSchemeFromUrl(url)}\n +AUTHORIZATION REQUEST : +${response != null ? const JsonEncoder.withIndent(' ').convert(response) : 'None'}\n +CLIENT METADATA : +${clientMetaData != null ? const JsonEncoder.withIndent(' ').convert(clientMetaData) : 'None'}\n +PRESENTATION DEFINITION : +${presentationDefinition != null ? const JsonEncoder.withIndent(' ').convert(presentationDefinition) : 'None'}\n +REGISTRATION : +${registrationMap != null ? const JsonEncoder.withIndent(' ').convert(registrationMap) : 'None'} '''; + + return data; } String getSchemeFromUrl(String url) { @@ -1052,3 +1106,89 @@ Future fetchRequestUriPayload({ } return data; } + +String getUpdatedUrlForSIOPV2OIC4VP({ + required String url, + required Map response, +}) { + final responseType = response['response_type']; + final redirectUri = response['redirect_uri']; + final scope = response['scope']; + final responseUri = response['response_uri']; + final responseMode = response['response_mode']; + final nonce = response['nonce']; + final clientId = response['client_id']; + final claims = response['claims']; + final stateValue = response['state']; + final presentationDefinition = response['presentation_definition']; + final presentationDefinitionUri = response['presentation_definition_uri']; + final registration = response['registration']; + final clientMetadata = response['client_metadata']; + final clientMetadataUri = response['client_metadata_uri']; + + final queryJson = {}; + + if (scope != null) { + queryJson['scope'] = scope; + } + + if (clientId != null) { + queryJson['client_id'] = clientId; + } + + if (redirectUri != null) { + queryJson['redirect_uri'] = redirectUri; + } + + if (responseUri != null) { + queryJson['response_uri'] = responseUri; + } + + if (responseMode != null) { + queryJson['response_mode'] = responseMode; + } + + if (nonce != null) { + queryJson['nonce'] = nonce; + } + + if (stateValue != null) { + queryJson['state'] = stateValue; + } + + if (responseType != null) { + queryJson['response_type'] = responseType; + } + + if (claims != null) { + queryJson['claims'] = jsonEncode(claims).replaceAll('"', "'"); + } + + if (presentationDefinition != null) { + queryJson['presentation_definition'] = + jsonEncode(presentationDefinition).replaceAll('"', "'"); + } + + if (presentationDefinitionUri != null) { + queryJson['presentation_definition_uri'] = presentationDefinitionUri; + } + + if (registration != null) { + queryJson['registration'] = + registration is Map ? jsonEncode(registration) : registration; + } + + if (clientMetadata != null) { + queryJson['client_metadata'] = + jsonEncode(clientMetadata).replaceAll('"', "'"); + } + + if (clientMetadataUri != null) { + queryJson['client_metadata_uri'] = clientMetadataUri; + } + + final String queryString = Uri(queryParameters: queryJson).query; + + final String newUrl = '$url&$queryString'; + return newUrl; +} diff --git a/lib/app/shared/widget/grouped_section.dart b/lib/app/shared/widget/grouped_section.dart index 830caf9cd..1b0adb90a 100644 --- a/lib/app/shared/widget/grouped_section.dart +++ b/lib/app/shared/widget/grouped_section.dart @@ -16,7 +16,7 @@ class GroupedSection extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceSmall), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.cardHighlighted, + color: Theme.of(context).colorScheme.drawerSurface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.largeRadius), ), diff --git a/lib/dashboard/drawer/src/widgets/drawer_item.dart b/lib/dashboard/drawer/src/widgets/drawer_item.dart index 68c6a7043..89b93bd04 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_item.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_item.dart @@ -33,53 +33,57 @@ class DrawerItem extends StatelessWidget { ), ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: Theme.of(context).textTheme.drawerItemTitle.copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), - ), - if (subtitle != null) ...[ - const SizedBox(height: 10), + child: SizedBox( + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Text( - subtitle!, - style: Theme.of(context) - .textTheme - .drawerItemSubtitle - .copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), + title, + style: + Theme.of(context).textTheme.drawerItemTitle.copyWith( + color: isDisabled + ? Theme.of(context).colorScheme.lightGrey + : null, + ), ), - const SizedBox(height: 10), + if (subtitle != null) ...[ + const SizedBox(height: 10), + Text( + subtitle!, + style: Theme.of(context) + .textTheme + .drawerItemSubtitle + .copyWith( + color: isDisabled + ? Theme.of(context).colorScheme.lightGrey + : null, + ), + ), + const SizedBox(height: 10), + ], ], - ], - ), - ), - if (trailing != null) - trailing! - else ...[ - const SizedBox(width: 16), - Icon( - Icons.chevron_right, - size: Sizes.icon2x, - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : Theme.of(context).colorScheme.unSelectedLabel, + ), ), + if (trailing != null) + trailing! + else ...[ + const SizedBox(width: 16), + Icon( + Icons.chevron_right, + size: Sizes.icon2x, + color: isDisabled + ? Theme.of(context).colorScheme.lightGrey + : Theme.of(context).colorScheme.unSelectedLabel, + ), + ], ], - ], + ), ), ), ); diff --git a/lib/dashboard/drawer/src/widgets/drawer_item2.dart b/lib/dashboard/drawer/src/widgets/drawer_item2.dart index 142c84332..1429f401d 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_item2.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_item2.dart @@ -35,30 +35,40 @@ class DrawerItem2 extends StatelessWidget { ), child: SizedBox( width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: Theme.of(context).textTheme.drawerItemTitle.copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: + Theme.of(context).textTheme.drawerItemTitle.copyWith( + color: isDisabled + ? Theme.of(context).colorScheme.lightGrey + : null, + ), ), - ), - if (subtitle != null) ...[ - const SizedBox(height: 10), - Text( - subtitle!, - style: - Theme.of(context).textTheme.drawerItemSubtitle.copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), + if (subtitle != null) ...[ + const SizedBox(height: 10), + Text( + subtitle!, + style: Theme.of(context) + .textTheme + .drawerItemSubtitle + .copyWith( + color: isDisabled + ? Theme.of(context).colorScheme.lightGrey + : null, + ), + ), + const SizedBox(height: 20), + ], + ], ), - const SizedBox(height: 20), - ], + ), if (trailing != null) trailing! else ...[ diff --git a/lib/dashboard/drawer/ssi/advanced_settings2/advanced_settings2.dart b/lib/dashboard/drawer/ssi/advanced_settings2/advanced_settings2.dart new file mode 100644 index 000000000..a96158314 --- /dev/null +++ b/lib/dashboard/drawer/ssi/advanced_settings2/advanced_settings2.dart @@ -0,0 +1,4 @@ +export 'manage_issuers_registry/verifiable_data_registry.dart'; +export 'oidc4vc_profile/oidc4vc_profile.dart'; +export 'oidc4vc_settngs/oidc4vc_settings.dart'; +export 'src/src.dart'; diff --git a/lib/dashboard/drawer/ssi/manage_issuers_registry/verifiable_data_registry.dart b/lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/verifiable_data_registry.dart similarity index 100% rename from lib/dashboard/drawer/ssi/manage_issuers_registry/verifiable_data_registry.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/verifiable_data_registry.dart diff --git a/lib/dashboard/drawer/ssi/manage_issuers_registry/view/verifiable_data_registry_page.dart b/lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/view/verifiable_data_registry_page.dart similarity index 100% rename from lib/dashboard/drawer/ssi/manage_issuers_registry/view/verifiable_data_registry_page.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/view/verifiable_data_registry_page.dart diff --git a/lib/dashboard/drawer/ssi/manage_issuers_registry/widgets/issuer_verifier_selector.dart b/lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/widgets/issuer_verifier_selector.dart similarity index 100% rename from lib/dashboard/drawer/ssi/manage_issuers_registry/widgets/issuer_verifier_selector.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/widgets/issuer_verifier_selector.dart diff --git a/lib/dashboard/drawer/ssi/manage_issuers_registry/widgets/widgets.dart b/lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/widgets/widgets.dart similarity index 100% rename from lib/dashboard/drawer/ssi/manage_issuers_registry/widgets/widgets.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/manage_issuers_registry/widgets/widgets.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_profile/oidc4vc_profile.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_profile/oidc4vc_profile.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_profile/oidc4vc_profile.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_profile/oidc4vc_profile.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_profile/view/oidc4vc_profile_page.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_profile/view/oidc4vc_profile_page.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_profile/view/oidc4vc_profile_page.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_profile/view/oidc4vc_profile_page.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/oidc4vc_settings.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/oidc4vc_settings.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/oidc4vc_settings.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/oidc4vc_settings.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/view/oidc4vc_settings_menu.dart similarity index 96% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/view/oidc4vc_settings_menu.dart index 9b1dadd60..4be1d2e8f 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart +++ b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/view/oidc4vc_settings_menu.dart @@ -39,6 +39,7 @@ class Oidc4vcSettingMenuView extends StatelessWidget { SixOrForUserPinWidget(), DidKeyTypeWidget(), SubjectSyntaxTypeWidget(), + CryptographicHolderBindingWidget(), ], ), ); diff --git a/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/cryptograhic_holder_binding.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/cryptograhic_holder_binding.dart new file mode 100644 index 000000000..0743966b6 --- /dev/null +++ b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/cryptograhic_holder_binding.dart @@ -0,0 +1,70 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/profile/profile.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:altme/theme/theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class CryptographicHolderBindingWidget extends StatelessWidget { + const CryptographicHolderBindingWidget({super.key}); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return BlocBuilder( + builder: (context, state) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + padding: const EdgeInsets.all(Sizes.spaceSmall), + margin: const EdgeInsets.all(Sizes.spaceXSmall), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.drawerSurface, + borderRadius: const BorderRadius.all( + Radius.circular(Sizes.largeRadius), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + l10n.cryptographicHolderBinding, + style: Theme.of(context).textTheme.drawerItemTitle, + ), + const SizedBox(height: 10), + Text( + l10n.cryptographicHolderBindingSubtitle, + style: Theme.of(context).textTheme.drawerItemSubtitle, + ), + ], + ), + ), + const SizedBox(height: 10), + Switch( + onChanged: (value) async { + await context + .read() + .updateCryptographicHolderBindingStatus( + enabled: value, + ); + }, + value: state.model.enableCryptographicHolderBinding, + activeColor: Theme.of(context).colorScheme.primary, + ), + ], + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/did_key_type_widget.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/did_key_type_widget.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/security_level_widget.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/security_level_widget.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/security_level_widget.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/security_level_widget.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/six_or_four_pin_widget.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/six_or_four_pin_widget.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/six_or_four_pin_widget.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/six_or_four_pin_widget.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/subject_syntax_type_widget.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/subject_syntax_type_widget.dart similarity index 100% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/subject_syntax_type_widget.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/subject_syntax_type_widget.dart diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/widget.dart similarity index 77% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart rename to lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/widget.dart index 1e5914abf..47268c067 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart +++ b/lib/dashboard/drawer/ssi/advanced_settings2/oidc4vc_settngs/widget/widget.dart @@ -1,3 +1,4 @@ +export 'cryptograhic_holder_binding.dart'; export 'did_key_type_widget.dart'; export 'security_level_widget.dart'; export 'six_or_four_pin_widget.dart'; diff --git a/lib/dashboard/drawer/ssi/advanced_settings2/src/src.dart b/lib/dashboard/drawer/ssi/advanced_settings2/src/src.dart new file mode 100644 index 000000000..a9970189d --- /dev/null +++ b/lib/dashboard/drawer/ssi/advanced_settings2/src/src.dart @@ -0,0 +1 @@ +export 'view/advance_settings2_menu.dart'; diff --git a/lib/dashboard/drawer/ssi/advanced_settings2/src/view/advance_settings2_menu.dart b/lib/dashboard/drawer/ssi/advanced_settings2/src/view/advance_settings2_menu.dart new file mode 100644 index 000000000..55dbc5ea7 --- /dev/null +++ b/lib/dashboard/drawer/ssi/advanced_settings2/src/view/advance_settings2_menu.dart @@ -0,0 +1,75 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; + +import 'package:altme/l10n/l10n.dart'; +import 'package:altme/theme/theme.dart'; +import 'package:flutter/material.dart'; + +class AdvancedSettings2Menu extends StatelessWidget { + const AdvancedSettings2Menu({super.key}); + + static Route route() { + return MaterialPageRoute( + settings: const RouteSettings(name: '/AdvancedSettings2Menu'), + builder: (_) => const AdvancedSettings2Menu(), + ); + } + + @override + Widget build(BuildContext context) { + return const AdvancedSettings2View(); + } +} + +class AdvancedSettings2View extends StatelessWidget { + const AdvancedSettings2View({super.key}); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return Drawer( + backgroundColor: Theme.of(context).colorScheme.drawerBackground, + child: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BackLeadingButton( + padding: EdgeInsets.zero, + color: Theme.of(context).colorScheme.onPrimary, + ), + const Center( + child: AltMeLogo(size: 90), + ), + const SizedBox(height: Sizes.spaceSmall), + // DrawerItem( + // title: l10n.oidc4vcProfile, + // onTap: () async { + // await Navigator.of(context) + // .push(OIDC4VCProfilePage.route()); + // }, + // ), + DrawerItem( + title: l10n.oidc4vc_settings, + onTap: () { + Navigator.of(context) + .push(Oidc4vcSettingMenu.route()); + }, + ), + DrawerItem( + title: l10n.verifiableDataRegistry, + onTap: () async { + await Navigator.of(context) + .push(VerifiableDataRegistryPage.route()); + }, + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/dashboard/drawer/ssi/advanced_settings2/src/view/src.dart b/lib/dashboard/drawer/ssi/advanced_settings2/src/view/src.dart new file mode 100644 index 000000000..a4c11e78e --- /dev/null +++ b/lib/dashboard/drawer/ssi/advanced_settings2/src/view/src.dart @@ -0,0 +1 @@ +export 'advance_settings2_menu.dart'; diff --git a/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart b/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart index 833afd41b..dfaf26198 100644 --- a/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart +++ b/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart @@ -64,20 +64,6 @@ class SSIView extends StatelessWidget { await Navigator.of(context).push(RestoreMenu.route()); }, ), - // DrawerItem( - // title: l10n.oidc4vcProfile, - // onTap: () async { - // await Navigator.of(context) - // .push(OIDC4VCProfilePage.route()); - // }, - // ), - DrawerItem( - title: l10n.verifiableDataRegistry, - onTap: () async { - await Navigator.of(context) - .push(VerifiableDataRegistryPage.route()); - }, - ), DrawerItem( title: l10n.searchCredentials, onTap: () { @@ -85,10 +71,10 @@ class SSIView extends StatelessWidget { }, ), DrawerItem( - title: l10n.oidc4vc_settings, - onTap: () { - Navigator.of(context) - .push(Oidc4vcSettingMenu.route()); + title: l10n.advanceSettings, + onTap: () async { + await Navigator.of(context) + .push(AdvancedSettings2Menu.route()); }, ), ], diff --git a/lib/dashboard/drawer/ssi/ssi.dart b/lib/dashboard/drawer/ssi/ssi.dart index cc5fccd38..7ad0c6f18 100644 --- a/lib/dashboard/drawer/ssi/ssi.dart +++ b/lib/dashboard/drawer/ssi/ssi.dart @@ -1,6 +1,5 @@ +export 'advanced_settings2/advanced_settings2.dart'; export 'backup/backup.dart'; export 'manage_did/manage_did.dart'; -export 'manage_issuers_registry/verifiable_data_registry.dart'; -export 'oidc4vc_settngs/oidc4vc_settings.dart'; export 'restore/restore.dart'; export 'src/src.dart'; diff --git a/lib/dashboard/drawer/wallet_security/advanced_security_settings/advanced_security_settings.dart b/lib/dashboard/drawer/wallet_security/advanced_security_settings/advanced_security_settings.dart new file mode 100644 index 000000000..28f57c505 --- /dev/null +++ b/lib/dashboard/drawer/wallet_security/advanced_security_settings/advanced_security_settings.dart @@ -0,0 +1 @@ +export 'view/advanced_security_settings_menu.dart'; diff --git a/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart b/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart new file mode 100644 index 000000000..c4d66c942 --- /dev/null +++ b/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart @@ -0,0 +1,112 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:altme/theme/theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AdvancedSecuritySettingsMenu extends StatelessWidget { + const AdvancedSecuritySettingsMenu({super.key}); + + static Route route() { + return MaterialPageRoute( + builder: (_) => const AdvancedSecuritySettingsMenu(), + settings: const RouteSettings(name: '/AdvancedSecuritySettingsMenu'), + ); + } + + @override + Widget build(BuildContext context) { + return const AdvancedSecuritySettingsView(); + } +} + +class AdvancedSecuritySettingsView extends StatelessWidget { + const AdvancedSecuritySettingsView({super.key}); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return BlocBuilder( + builder: (context, state) { + return Drawer( + backgroundColor: Theme.of(context).colorScheme.drawerBackground, + child: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BackLeadingButton( + padding: EdgeInsets.zero, + color: Theme.of(context).colorScheme.onPrimary, + ), + const Center( + child: AltMeLogo(size: 90), + ), + const SizedBox( + height: Sizes.spaceSmall, + ), + DrawerItem2( + title: l10n.verifyIssuerWebsiteIdentity, + subtitle: l10n.verifyIssuerWebsiteIdentitySubtitle, + trailing: SizedBox( + height: 25, + child: Switch( + onChanged: (value) async { + await context + .read() + .setUserConsentForIssuerAccess(enabled: value); + }, + value: state.model.userConsentForIssuerAccess, + activeColor: Theme.of(context).colorScheme.primary, + ), + ), + ), + DrawerItem2( + title: l10n.confirmVerifierAccess, + subtitle: l10n.confirmVerifierAccessSubtitle, + trailing: SizedBox( + height: 25, + child: Switch( + onChanged: (value) async { + await context + .read() + .setUserConsentForVerifierAccess( + enabled: value, + ); + }, + value: state.model.userConsentForVerifierAccess, + activeColor: Theme.of(context).colorScheme.primary, + ), + ), + ), + DrawerItem2( + title: l10n.secureAuthenticationWithPINCode, + subtitle: l10n.secureAuthenticationWithPINCodeSubtitle, + trailing: SizedBox( + height: 25, + child: Switch( + onChanged: (value) async { + await context + .read() + .setUserPINCodeForAuthentication( + enabled: value, + ); + }, + value: state.model.userPINCodeForAuthentication, + activeColor: Theme.of(context).colorScheme.primary, + ), + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/dashboard/drawer/wallet_security/wallet_security.dart b/lib/dashboard/drawer/wallet_security/wallet_security.dart index 2404ae08d..f87820b3a 100644 --- a/lib/dashboard/drawer/wallet_security/wallet_security.dart +++ b/lib/dashboard/drawer/wallet_security/wallet_security.dart @@ -1,2 +1,3 @@ +export 'advanced_security_settings/advanced_security_settings.dart'; export 'recovery_key/recovery_key.dart'; export 'wallet_security/wallet_security.dart'; diff --git a/lib/dashboard/drawer/wallet_security/wallet_security/view/wallet_security_menu.dart b/lib/dashboard/drawer/wallet_security/wallet_security/view/wallet_security_menu.dart index e97ac0585..ea36d6056 100644 --- a/lib/dashboard/drawer/wallet_security/wallet_security/view/wallet_security_menu.dart +++ b/lib/dashboard/drawer/wallet_security/wallet_security/view/wallet_security_menu.dart @@ -136,58 +136,6 @@ class WalletSecurityView extends StatelessWidget { ), ), ), - DrawerItem2( - title: l10n.verifyIssuerWebsiteIdentity, - subtitle: l10n.verifyIssuerWebsiteIdentitySubtitle, - trailing: SizedBox( - height: 25, - child: Switch( - onChanged: (value) async { - await context - .read() - .setUserConsentForIssuerAccess(enabled: value); - }, - value: state.model.userConsentForIssuerAccess, - activeColor: Theme.of(context).colorScheme.primary, - ), - ), - ), - DrawerItem2( - title: l10n.confirmVerifierAccess, - subtitle: l10n.confirmVerifierAccessSubtitle, - trailing: SizedBox( - height: 25, - child: Switch( - onChanged: (value) async { - await context - .read() - .setUserConsentForVerifierAccess( - enabled: value, - ); - }, - value: state.model.userConsentForVerifierAccess, - activeColor: Theme.of(context).colorScheme.primary, - ), - ), - ), - DrawerItem2( - title: l10n.secureAuthenticationWithPINCode, - subtitle: l10n.secureAuthenticationWithPINCodeSubtitle, - trailing: SizedBox( - height: 25, - child: Switch( - onChanged: (value) async { - await context - .read() - .setUserPINCodeForAuthentication( - enabled: value, - ); - }, - value: state.model.userPINCodeForAuthentication, - activeColor: Theme.of(context).colorScheme.primary, - ), - ), - ), DrawerItem( title: l10n.showWalletRecoveryPhrase, subtitle: l10n.showWalletRecoveryPhraseSubtitle, @@ -220,6 +168,14 @@ class WalletSecurityView extends StatelessWidget { } }, ), + DrawerItem( + title: l10n.advancedSecuritySettings, + onTap: () { + Navigator.of(context).push( + AdvancedSecuritySettingsMenu.route(), + ); + }, + ), ], ), ), diff --git a/lib/dashboard/json_viewer/view/json_viewer_page.dart b/lib/dashboard/json_viewer/view/json_viewer_page.dart index 0c85250d0..2a6be26e7 100644 --- a/lib/dashboard/json_viewer/view/json_viewer_page.dart +++ b/lib/dashboard/json_viewer/view/json_viewer_page.dart @@ -44,15 +44,45 @@ class JsonViewerView extends StatelessWidget { @override Widget build(BuildContext context) { + final pattern = RegExp(r'(.*?)<\/b>'); + final matches = pattern.allMatches(data); + + final textSpans = []; + int currentIndex = 0; + + for (final match in matches) { + final plainText = data.substring(currentIndex, match.start); + final boldText = data.substring(match.start + 3, match.end - 4); + textSpans.add( + TextSpan( + text: plainText, + style: Theme.of(context).textTheme.bodyMedium, + ), + ); + textSpans.add( + TextSpan( + text: boldText, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontWeight: FontWeight.w700, + ), + ), + ); + currentIndex = match.end; + } + + if (currentIndex < data.length) { + final remainingText = data.substring(currentIndex); + textSpans.add(TextSpan(text: remainingText)); + } + return BasePage( title: title, titleAlignment: Alignment.topCenter, scrollView: true, titleLeading: const BackLeadingButton(), padding: const EdgeInsets.symmetric(horizontal: 10), - body: Text( - data, - style: Theme.of(context).textTheme.bodyMedium, + body: RichText( + text: TextSpan(children: textSpans), ), ); } diff --git a/lib/dashboard/profile/cubit/profile_cubit.dart b/lib/dashboard/profile/cubit/profile_cubit.dart index 0f3624673..248721faa 100644 --- a/lib/dashboard/profile/cubit/profile_cubit.dart +++ b/lib/dashboard/profile/cubit/profile_cubit.dart @@ -120,6 +120,13 @@ class ProfileCubit extends Cubit { final enableJWKThumbprint = enableJWKThumbprintValue != null && enableJWKThumbprintValue == 'true'; + final enableCryptographicHolderBindingValue = await secureStorageProvider + .get(SecureStorageKeys.enableCryptographicHolderBinding); + + final enableCryptographicHolderBinding = + enableCryptographicHolderBindingValue == null || + enableCryptographicHolderBindingValue == 'true'; + final userPINCodeForAuthenticationValue = await secureStorageProvider .get(SecureStorageKeys.userPINCodeForAuthentication); final userPINCodeForAuthentication = @@ -153,6 +160,7 @@ class ProfileCubit extends Cubit { isDeveloperMode: isDeveloperMode, enable4DigitPINCode: enable4DigitPINCode, enableJWKThumbprint: enableJWKThumbprint, + enableCryptographicHolderBinding: enableCryptographicHolderBinding, ); await update(profileModel); } catch (e, s) { @@ -262,6 +270,11 @@ class ProfileCubit extends Cubit { profileModel.enableJWKThumbprint.toString(), ); + await secureStorageProvider.set( + SecureStorageKeys.enableCryptographicHolderBinding, + profileModel.enableCryptographicHolderBinding.toString(), + ); + emit( state.copyWith( model: profileModel, @@ -342,6 +355,14 @@ class ProfileCubit extends Cubit { await update(profileModel); } + Future updateCryptographicHolderBindingStatus({ + bool enabled = false, + }) async { + final profileModel = + state.model.copyWith(enableCryptographicHolderBinding: enabled); + await update(profileModel); + } + Future enable4DigitPINCode({bool enabled = false}) async { final profileModel = state.model.copyWith(enable4DigitPINCode: enabled); await update(profileModel); diff --git a/lib/dashboard/profile/models/profile.dart b/lib/dashboard/profile/models/profile.dart index 4301ce134..5d6b18d6c 100644 --- a/lib/dashboard/profile/models/profile.dart +++ b/lib/dashboard/profile/models/profile.dart @@ -21,6 +21,7 @@ class ProfileModel extends Equatable { required this.userConsentForVerifierAccess, required this.userPINCodeForAuthentication, required this.enableJWKThumbprint, + required this.enableCryptographicHolderBinding, this.companyName = '', this.companyWebsite = '', this.jobTitle = '', @@ -53,6 +54,7 @@ class ProfileModel extends Equatable { isDeveloperMode: false, enable4DigitPINCode: false, enableJWKThumbprint: false, + enableCryptographicHolderBinding: true, ); final String firstName; @@ -76,6 +78,7 @@ class ProfileModel extends Equatable { final bool isDeveloperMode; final bool enable4DigitPINCode; final bool enableJWKThumbprint; + final bool enableCryptographicHolderBinding; @override List get props => [ @@ -99,6 +102,7 @@ class ProfileModel extends Equatable { isDeveloperMode, enable4DigitPINCode, enableJWKThumbprint, + enableCryptographicHolderBinding, ]; Map toJson() => _$ProfileModelToJson(this); @@ -124,6 +128,7 @@ class ProfileModel extends Equatable { bool? isDeveloperMode, bool? enable4DigitPINCode, bool? enableJWKThumbprint, + bool? enableCryptographicHolderBinding, }) { return ProfileModel( firstName: firstName ?? this.firstName, @@ -140,6 +145,8 @@ class ProfileModel extends Equatable { isEnterprise: isEnterprise ?? this.isEnterprise, isBiometricEnabled: isBiometricEnabled ?? this.isBiometricEnabled, enableJWKThumbprint: enableJWKThumbprint ?? this.enableJWKThumbprint, + enableCryptographicHolderBinding: enableCryptographicHolderBinding ?? + this.enableCryptographicHolderBinding, userConsentForIssuerAccess: userConsentForIssuerAccess ?? this.userConsentForIssuerAccess, userConsentForVerifierAccess: 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 331849f6c..8326e0459 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 @@ -436,6 +436,8 @@ class QRCodeScanCubit extends Cubit { oidc4vc: oidc4vc, isEBSIV3: isEBSIV3, credentialOfferJson: credentialOfferJson, + sendProof: profileCubit + .state.model.enableCryptographicHolderBinding, ); }, ), @@ -457,87 +459,28 @@ class QRCodeScanCubit extends Cubit { userPin: null, credentialOfferJson: credentialOfferJson, isEBSIV3: isEBSIV3, + sendProof: profileCubit.state.model.enableCryptographicHolderBinding, ); } Future startSIOPV2OIDC4VPProcess(Uri uri) async { final String? requestUri = uri.queryParameters['request_uri']; final String? request = uri.queryParameters['request']; - dynamic responseType; /// check if request uri is provided or not if (requestUri != null || request != null) { /// verifier side (oidc4vp) or (siopv2 oidc4vc) with request_uri /// afer verification process - final Map response = - decodePayload(jwtDecode: jwtDecode, token: encodedData as String); - encodedData = null; - - responseType = response['response_type']; - final redirectUri = response['redirect_uri']; - final scope = response['scope']; - final responseUri = response['response_uri']; - final responseMode = response['response_mode']; - final nonce = response['nonce']; - final clientId = response['client_id']; - final claims = response['claims']; - final stateValue = response['state']; - final presentationDefinition = response['presentation_definition']; - final presentationDefinitionUri = response['presentation_definition_uri']; - final registration = response['registration']; - - final queryJson = {}; - - if (scope != null) { - queryJson['scope'] = scope; - } - - if (clientId != null) { - queryJson['client_id'] = clientId; - } - - if (redirectUri != null) { - queryJson['redirect_uri'] = redirectUri; - } - - if (responseUri != null) { - queryJson['response_uri'] = responseUri; - } - - if (responseMode != null) { - queryJson['response_mode'] = responseMode; - } - - if (nonce != null) { - queryJson['nonce'] = nonce; - } - - if (stateValue != null) { - queryJson['state'] = stateValue; - } - if (responseType != null) { - queryJson['response_type'] = responseType; - } - if (claims != null) { - queryJson['claims'] = jsonEncode(claims).replaceAll('"', "'"); - } - if (presentationDefinition != null) { - queryJson['presentation_definition'] = - jsonEncode(presentationDefinition).replaceAll('"', "'"); - } - - if (presentationDefinitionUri != null) { - queryJson['presentation_definition_uri'] = presentationDefinitionUri; - } - - if (registration != null) { - queryJson['registration'] = - registration is Map ? jsonEncode(registration) : registration; - } - - final String queryString = Uri(queryParameters: queryJson).query; + final Map response = decodePayload( + jwtDecode: jwtDecode, + token: encodedData as String, + ); - final String newUrl = '$uri&$queryString'; + final String newUrl = getUpdatedUrlForSIOPV2OIC4VP( + url: uri.toString(), + response: response, + ); + encodedData = null; emit( state.copyWith( @@ -546,10 +489,10 @@ class QRCodeScanCubit extends Cubit { ), ); log.i('uri - $newUrl'); - } else { - responseType = uri.queryParameters['response_type'] ?? ''; } + final responseType = state.uri?.queryParameters['response_type'] ?? ''; + /// check required keys available or not final keys = []; state.uri?.queryParameters.forEach((key, value) => keys.add(key)); @@ -1168,6 +1111,7 @@ class QRCodeScanCubit extends Cubit { preAuthorizedCode: preAuthorizedCode, codeForAuthorisedFlow: codeForAuthorisedFlow, codeVerifier: codeVerifier, + sendProof: profileCubit.state.model.enableCryptographicHolderBinding, ); } diff --git a/lib/dashboard/src/widgets/what_is_new_dialog.dart b/lib/dashboard/src/widgets/what_is_new_dialog.dart index 2a6bea696..12cfe09c4 100644 --- a/lib/dashboard/src/widgets/what_is_new_dialog.dart +++ b/lib/dashboard/src/widgets/what_is_new_dialog.dart @@ -76,6 +76,7 @@ class WhatIsNewDialog extends StatelessWidget { NewContent( version: versionNumber, features: const [ + 'Developer mode improvement', 'Bug correction', ], ), diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index dd24e8342..7ffb03960 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -982,6 +982,8 @@ "selectOneOfTheDid": "Select one of the DIDs", "subjectSyntaxType": "Subject syntax type", "enableToUseTheJWKThumprintOfTheKey": "Default: DID\nEnable to use the JWK thumprint of the key", + "cryptographicHolderBinding": "Cryptographic holder binding", + "cryptographicHolderBindingSubtitle": "Default : On\nDisable to accept Bearer credentials as tickets with low assurance.", "theServiceIsNotAvailable": "The service is not available", "download": "Download", "issuerDID": "Issuer DID", @@ -992,6 +994,7 @@ "revokedOrSuspendedCredential": "Revoked or Suspended Credential", "displayConfiguration": "Display configuration", "downloadConfiguration": "Download configuration", - "successfullyDownloaded": "Successfully Downloaded" + "successfullyDownloaded": "Successfully Downloaded", + "advancedSecuritySettings": "Advanced Security Settings" } diff --git a/lib/l10n/untranslated.json b/lib/l10n/untranslated.json index 2f7ee5a42..8f8360546 100644 --- a/lib/l10n/untranslated.json +++ b/lib/l10n/untranslated.json @@ -888,6 +888,8 @@ "selectOneOfTheDid", "subjectSyntaxType", "enableToUseTheJWKThumprintOfTheKey", + "cryptographicHolderBinding", + "cryptographicHolderBindingSubtitle", "theServiceIsNotAvailable", "download", "issuerDID", @@ -898,7 +900,8 @@ "revokedOrSuspendedCredential", "displayConfiguration", "downloadConfiguration", - "successfullyDownloaded" + "successfullyDownloaded", + "advancedSecuritySettings" ], "es": [ @@ -1790,6 +1793,8 @@ "selectOneOfTheDid", "subjectSyntaxType", "enableToUseTheJWKThumprintOfTheKey", + "cryptographicHolderBinding", + "cryptographicHolderBindingSubtitle", "theServiceIsNotAvailable", "download", "issuerDID", @@ -1800,7 +1805,8 @@ "revokedOrSuspendedCredential", "displayConfiguration", "downloadConfiguration", - "successfullyDownloaded" + "successfullyDownloaded", + "advancedSecuritySettings" ], "fr": [ @@ -1995,6 +2001,8 @@ "selectOneOfTheDid", "subjectSyntaxType", "enableToUseTheJWKThumprintOfTheKey", + "cryptographicHolderBinding", + "cryptographicHolderBindingSubtitle", "theServiceIsNotAvailable", "download", "issuerDID", @@ -2005,7 +2013,8 @@ "revokedOrSuspendedCredential", "displayConfiguration", "downloadConfiguration", - "successfullyDownloaded" + "successfullyDownloaded", + "advancedSecuritySettings" ], "it": [ @@ -2897,6 +2906,8 @@ "selectOneOfTheDid", "subjectSyntaxType", "enableToUseTheJWKThumprintOfTheKey", + "cryptographicHolderBinding", + "cryptographicHolderBindingSubtitle", "theServiceIsNotAvailable", "download", "issuerDID", @@ -2907,6 +2918,7 @@ "revokedOrSuspendedCredential", "displayConfiguration", "downloadConfiguration", - "successfullyDownloaded" + "successfullyDownloaded", + "advancedSecuritySettings" ] } diff --git a/lib/oidc4vc/get_and_add_credential.dart b/lib/oidc4vc/get_and_add_credential.dart index 832bad7d7..5240e0ab4 100644 --- a/lib/oidc4vc/get_and_add_credential.dart +++ b/lib/oidc4vc/get_and_add_credential.dart @@ -23,6 +23,7 @@ Future getAndAddCredential({ required String issuer, required String? codeForAuthorisedFlow, required String? codeVerifier, + required bool sendProof, }) async { final privateKey = await fetchPrivateKey( isEBSIV3: isEBSIV3, @@ -60,6 +61,7 @@ Future getAndAddCredential({ userPin: userPin, code: codeForAuthorisedFlow, codeVerifier: codeVerifier, + sendProof: sendProof, ); for (int i = 0; i < encodedCredentialOrFutureTokens.length; i++) { diff --git a/lib/oidc4vc/initiate_oidv4vc_credential_issuance.dart b/lib/oidc4vc/initiate_oidv4vc_credential_issuance.dart index 62f74eff5..82eb375de 100644 --- a/lib/oidc4vc/initiate_oidv4vc_credential_issuance.dart +++ b/lib/oidc4vc/initiate_oidv4vc_credential_issuance.dart @@ -20,6 +20,7 @@ Future initiateOIDC4VCCredentialIssuance({ required DioClient dioClient, required String? userPin, required dynamic credentialOfferJson, + required bool sendProof, }) async { final Uri uriFromScannedResponse = Uri.parse(scannedResponse); @@ -109,6 +110,7 @@ Future initiateOIDC4VCCredentialIssuance({ preAuthorizedCode: preAuthorizedCode, codeForAuthorisedFlow: null, codeVerifier: null, + sendProof: sendProof, ); oidc4vc.resetNonceAndAccessTokenAndAuthorizationDetails(); qrCodeScanCubit.goBack(); diff --git a/lib/splash/bloclisteners/blocklisteners.dart b/lib/splash/bloclisteners/blocklisteners.dart index aec367545..05250383a 100644 --- a/lib/splash/bloclisteners/blocklisteners.dart +++ b/lib/splash/bloclisteners/blocklisteners.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:altme/app/app.dart'; import 'package:altme/connection_bridge/connection_bridge.dart'; @@ -16,12 +15,12 @@ import 'package:altme/splash/splash.dart'; import 'package:altme/wallet/wallet.dart'; import 'package:beacon_flutter/beacon_flutter.dart'; import 'package:dio/dio.dart'; -import 'package:file_saver/file_saver.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:polygonid/polygonid.dart'; +import 'package:share_plus/share_plus.dart'; final splashBlocListener = BlocListener( listener: (BuildContext context, SplashState state) { @@ -275,6 +274,8 @@ final qrCodeBlocListener = BlocListener( state.uri!.queryParameters['request_uri']; final String? request = state.uri!.queryParameters['request']; + Map? response; + if (requestUri != null || request != null) { late dynamic encodedData; if (requestUri != null) { @@ -285,42 +286,22 @@ final qrCodeBlocListener = BlocListener( } else { encodedData = request; } - final Map response = decodePayload( + + response = decodePayload( jwtDecode: JWTDecode(), token: encodedData as String, ); - final presentationDefinition = - response['presentation_definition']; - final presentationDefinitionUri = - response['presentation_definition_uri']; - - final queryJson = {}; - - if (presentationDefinition != null) { - queryJson['presentation_definition'] = - jsonEncode(presentationDefinition).replaceAll('"', "'"); - } - - if (presentationDefinitionUri != null) { - queryJson['presentation_definition_uri'] = - presentationDefinitionUri; - } - - final String queryString = - Uri(queryParameters: queryJson).query; - - url = '${state.uri}}&$queryString'; + url = getUpdatedUrlForSIOPV2OIC4VP( + url: state.uri.toString(), + response: response, + ); } - final Map? presentationDefinitionData = - await getPresentationDefinition( + formattedData = await getFormattedStringOIDC4VPSIOPV2( + url: url, client: client, - uri: Uri.parse(url), - ); - formattedData = getFormattedStringOIDC4VPSIOPV2( - url: state.uri.toString(), - presentationDefinition: presentationDefinitionData, + response: response, ); } @@ -339,42 +320,18 @@ final qrCodeBlocListener = BlocListener( ); return; }, - onDownload: () async { + onDownload: () { Navigator.of(context).pop(false); - final isPermissionStatusGranted = - await getStoragePermission(); - if (!isPermissionStatusGranted) { - throw ResponseMessage( - message: ResponseString - .STORAGE_PERMISSION_DENIED_MESSAGE, - ); - } - - final dateTime = getDateTimeWithoutSpace(); - final fileName = 'oidc4vci-data-$dateTime'; - - final fileSaver = FileSaver.instance; - - final fileBytes = - Uint8List.fromList(utf8.encode(formattedData)); - - final filePath = await fileSaver.saveAs( - name: fileName, - bytes: fileBytes, - ext: 'txt', - mimeType: MimeType.text, + + final box = context.findRenderObject() as RenderBox?; + final subject = l10n.shareWith; + + Share.share( + formattedData, + subject: subject, + sharePositionOrigin: + box!.localToGlobal(Offset.zero) & box.size, ); - if (filePath != null && filePath.isEmpty) { - // - } else { - AlertMessage.showStateMessage( - context: context, - stateMessage: StateMessage.success( - showDialog: false, - stringMessage: l10n.successfullyDownloaded, - ), - ); - } }, onSkip: () { Navigator.of(context).pop(true); diff --git a/packages/oidc4vc/lib/src/oidc4vc.dart b/packages/oidc4vc/lib/src/oidc4vc.dart index aef0b0e54..c9e19f8db 100644 --- a/packages/oidc4vc/lib/src/oidc4vc.dart +++ b/packages/oidc4vc/lib/src/oidc4vc.dart @@ -200,21 +200,50 @@ class OIDC4VC { 'authorization_endpoint': authorizationEndPoint, 'scopes_supported': ['openid'], 'response_types_supported': ['vp_token', 'id_token'], + 'client_id_schemes_supported': ['redirect_uri', 'did'], + 'grant_types_supported': ['authorization_code', 'pre-authorized_code'], 'subject_types_supported': ['public'], - 'id_token_signing_alg_values_supported': ['ES256'], - 'request_object_signing_alg_values_supported': ['ES256'], + 'id_token_signing_alg_values_supported': ['ES256', 'ES256K'], + 'request_object_signing_alg_values_supported': [ + 'ES256', + 'ES256K', + ], + 'request_parameter_supported': true, + 'request_uri_parameter_supported': true, + 'request_authentication_methods_supported': { + 'authorization_endpoint': ['request_object'], + }, 'vp_formats_supported': { 'jwt_vp': { - 'alg_values_supported': ['ES256'], + 'alg_values_supported': [ + 'ES256', + 'ES256K', + ], }, 'jwt_vc': { - 'alg_values_supported': ['ES256'], + 'alg_values_supported': [ + 'ES256', + 'ES256K', + ], }, }, 'subject_syntax_types_supported': [ 'urn:ietf:params:oauth:jwk-thumbprint', + 'did:key', + 'did:ebsi', + 'did:tz', + 'did:pkh', + 'did:hedera', + 'did:key', + 'did:polygonid', + 'did:ethr', + 'did:web', + ], + 'subject_syntax_types_discriminations': [ 'did🔑jwk_jcs-pub', + 'did:ebsi:v1', ], + 'subject_trust_frameworks_supported': ['ebsi'], 'id_token_types_supported': ['subject_signed_id_token'], }), }; @@ -233,6 +262,7 @@ class OIDC4VC { required String kid, required int indexValue, required String privateKey, + required bool sendProof, String? preAuthorizedCode, String? userPin, String? code, @@ -307,6 +337,7 @@ class OIDC4VC { types: types, format: format, identifier: identifier, + sendProof: sendProof, ); credentialResponseData.add(credentialResponseDataValue); @@ -319,6 +350,7 @@ class OIDC4VC { credentialType: credentialType, types: types, format: format, + sendProof: sendProof, ); credentialResponseData.add(credentialResponseDataValue); @@ -333,6 +365,7 @@ class OIDC4VC { required String credentialType, required List types, required String format, + required bool sendProof, String? identifier, }) async { final credentialData = await buildCredentialData( @@ -343,6 +376,7 @@ class OIDC4VC { types: types, format: format, identifier: identifier, + sendProof: sendProof, ); /// sign proof @@ -507,6 +541,7 @@ class OIDC4VC { required String credentialType, required List types, required String format, + required bool sendProof, String? identifier, }) async { final vcJwt = await getIssuerJwt(issuerTokenParameters, nonce); @@ -515,11 +550,14 @@ class OIDC4VC { 'type': credentialType, 'types': types, 'format': format, - 'proof': { + }; + + if (sendProof) { + credentialData['proof'] = { 'proof_type': 'jwt', 'jwt': vcJwt, - }, - }; + }; + } if (identifier != null) { credentialData['identifier'] = identifier; diff --git a/pubspec.yaml b/pubspec.yaml index d538f1454..e8a56237e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: altme description: AltMe Flutter App -version: 1.21.15+300 +version: 1.23.0+302 environment: sdk: ">=3.1.0 <4.0.0"