diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart index e06a2ca07..44440bafc 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart @@ -63,7 +63,7 @@ List getCredentialsFromFilterList({ } } } - return selectedCredential; + return selectedCredential.toSet().toList(); } return credentialList; } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart index 3c1f5fca8..13e7e0182 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart @@ -42,7 +42,10 @@ class SelectiveDisclosureCubit extends Cubit { final searchList = getTextsFromCredential(path, credentialData); for (final element in searchList) { final key = path.split('.').toList().last; - json[key] = element; + json[key] = { + 'element': element, + 'optional': field.optional, + }; } } } @@ -81,20 +84,24 @@ class SelectiveDisclosureCubit extends Cubit { int? index; if (threeDotValue != null) { - for (final element - in selectiveDisclosure.disclosureToContent.entries.toList()) { + final disclosureToContentEntries = + selectiveDisclosure.disclosureToContent.entries.toList(); + + for (final element in disclosureToContentEntries) { final sh256Hash = oidc4vc.sh256HashOfContent(element.value.toString()); + if (sh256Hash == threeDotValue) { final disclosure = element.key.replaceAll('=', ''); - - index = selectiveDisclosure.disclosureFromJWT - .indexWhere((element) => element == disclosure); + index = disclosureToContentEntries + .indexWhere((entry) => entry.key == disclosure); + break; } } } else if (claimsKey != null) { - index = selectiveDisclosure.extractedValuesFromJwt.entries - .toList() - .indexWhere((entry) => entry.key == claimsKey); + index = + selectiveDisclosure.disclosureToContent.entries.toList().indexWhere( + (entry) => entry.value.toString().contains(claimsKey), + ); } if (index == null) { diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart index bd868cc05..e738ebf3d 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart @@ -5,7 +5,7 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/scan/cubit/scan_cubit.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; -import 'package:altme/selective_disclosure/widget/display_selective_disclosure.dart'; +import 'package:altme/selective_disclosure/widget/inject_selective_disclosure_state.dart'; import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -115,8 +115,8 @@ class _SelectiveDisclosurePickViewState ); } }, - child: BlocBuilder( - builder: (context, state) { + child: Builder( + builder: (BuildContext context) { final profileSetting = context.read().state.model.profileSetting; @@ -143,10 +143,8 @@ class _SelectiveDisclosurePickViewState isDiscover: false, ), const SizedBox(height: 20), - DisplaySelectiveDisclosure( + ConsumeSelectiveDisclosureCubit( credentialModel: widget.credentialToBePresented, - claims: null, - selectiveDisclosureState: state, onPressed: (claimKey, claimKeyId, threeDotValue) { context.read().disclosureAction( claimsKey: claimKey, @@ -164,13 +162,18 @@ class _SelectiveDisclosurePickViewState padding: const EdgeInsets.all(16), child: Tooltip( message: l10n.credentialPickPresent, - child: MyGradientButton( - onPressed: () => present( - context: context, - selectedSDIndexInJWT: state.selectedSDIndexInJWT, - uri: widget.uri, - ), - text: l10n.credentialPickPresent, + child: BlocBuilder( + builder: (context, state) { + return MyGradientButton( + onPressed: () => present( + context: context, + selectedSDIndexInJWT: state.selectedSDIndexInJWT, + uri: widget.uri, + ), + text: l10n.credentialPickPresent, + ); + }, ), ), ), @@ -210,6 +213,9 @@ class _SelectiveDisclosurePickViewState } } + final selectiveDisclosure = + SelectiveDisclosure(widget.credentialToBePresented); + final encryptedValues = widget.credentialToBePresented.jwt ?.split('~') .where((element) => element.isNotEmpty) @@ -218,10 +224,16 @@ class _SelectiveDisclosurePickViewState if (encryptedValues != null) { var newJwt = '${encryptedValues[0]}~'; - encryptedValues.removeAt(0); + // encryptedValues.removeAt(0); + + final organizedDisclosure = selectiveDisclosure + .disclosureToContent.entries + .toList() + .map((element) => element.key) + .toList(); for (final index in selectedSDIndexInJWT) { - newJwt = '$newJwt${encryptedValues[index]}~'; + newJwt = '$newJwt${organizedDisclosure[index]}~'; } // Key Binding JWT 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 ee81113eb..8d1822921 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 @@ -627,29 +627,6 @@ class QRCodeScanCubit extends Cubit { } } } - final clientType = profileCubit.state.model.profileSetting - .selfSovereignIdentityOptions.customOidc4vcProfile.clientType; - - if (clientType != ClientType.p256JWKThumprint) { - final clientMetadata = state.uri!.queryParameters['client_metadata']; - if (clientMetadata != null) { - final clientMetadataMap = - jsonDecode(clientMetadata) as Map; - final data = clientMetadataMap['subject_syntax_types_supported'] - as List; - if (!data.contains('did:key')) { - if (isSecurityHigh) { - throw ResponseMessage( - data: { - 'error': 'unsupported_response_type', - 'error_description': - 'The subject syntax type is not supported.', - }, - ); - } - } - } - } final redirectUri = state.uri!.queryParameters['redirect_uri']; final responseUri = state.uri!.queryParameters['response_uri']; diff --git a/lib/oidc4vc/add_oidc4vc_credential.dart b/lib/oidc4vc/add_oidc4vc_credential.dart index 78ffad433..22484f55e 100644 --- a/lib/oidc4vc/add_oidc4vc_credential.dart +++ b/lib/oidc4vc/add_oidc4vc_credential.dart @@ -32,9 +32,9 @@ Future addOIDC4VCCredential({ final jsonContent = jwtDecode.parseJwt(data); if (format == VCFormatType.vcSdJWT.vcValue) { - final sdAlg = jsonContent['_sd_alg']; + final sdAlg = jsonContent['_sd_alg']??'sha-256'; - if (sdAlg == null || sdAlg != 'sha-256') { + if (sdAlg != 'sha-256') { throw ResponseMessage( data: { 'error': 'invalid_request', diff --git a/lib/scan/cubit/scan_cubit.dart b/lib/scan/cubit/scan_cubit.dart index 6efdb7551..54d04f122 100644 --- a/lib/scan/cubit/scan_cubit.dart +++ b/lib/scan/cubit/scan_cubit.dart @@ -359,7 +359,7 @@ class ScanCubit extends Cubit { /// proof check to fail because of time difference on server final options = jsonEncode({ 'verificationMethod': kid, - 'proofPurpose': 'assertionMethod', + 'proofPurpose': 'authentication', 'challenge': challenge, 'domain': domain, 'created': DateTime.now() @@ -899,7 +899,7 @@ class ScanCubit extends Cubit { /// proof check to fail because of time difference on server final options = jsonEncode({ 'verificationMethod': kid, - 'proofPurpose': 'assertionMethod', + 'proofPurpose': 'authentication', 'challenge': nonce, 'domain': clientId, 'created': DateTime.now() diff --git a/lib/selective_disclosure/widget/display_selective_disclosure.dart b/lib/selective_disclosure/widget/display_selective_disclosure.dart index eed3b9ee0..5757ef2fc 100644 --- a/lib/selective_disclosure/widget/display_selective_disclosure.dart +++ b/lib/selective_disclosure/widget/display_selective_disclosure.dart @@ -129,6 +129,7 @@ class DisplaySelectiveDisclosure extends StatelessWidget { } bool? disable; + bool selected = false; if (selectiveDisclosureState != null) { final limitDisclosure = @@ -143,12 +144,42 @@ class DisplaySelectiveDisclosure extends StatelessWidget { filters.forEach((key, value) { if (claims.threeDotValue != null) { if (claimKey.contains(key) && - claims.data.replaceAll(' ', '') == value) { + claims.data.replaceAll(' ', '') == + value['element']) { disable = false; + selected = value['optional'] as bool; + if (!selectiveDisclosureState! + .selectedClaimsKeyIds + .contains(keyToCheck) && + selected == true) { + context + .read() + .disclosureAction( + claimsKey: key, + credentialModel: credentialModel, + threeDotValue: claims.threeDotValue, + claimKeyId: claimKey, + ); + } } } else { - if (claimKey == key && claims.data == value) { + if (claimKey == key && + claims.data == value['element']) { disable = false; + selected = !(value['optional'] as bool); + if (!selectiveDisclosureState! + .selectedClaimsKeyIds + .contains(keyToCheck) && + selected == true) { + context + .read() + .disclosureAction( + claimsKey: key, + credentialModel: credentialModel, + threeDotValue: claims.threeDotValue, + claimKeyId: claimKey, + ); + } } } }); @@ -159,7 +190,9 @@ class DisplaySelectiveDisclosure extends StatelessWidget { return TransparentInkWell( onTap: () { - if (disable != null && disable!) return; + if ((disable != null && disable!) || selected == true) { + return; + } onPressed?.call(key, claimKey, claims.threeDotValue); }, diff --git a/lib/selective_disclosure/widget/inject_selective_disclosure_state.dart b/lib/selective_disclosure/widget/inject_selective_disclosure_state.dart new file mode 100644 index 000000000..d8a040d9e --- /dev/null +++ b/lib/selective_disclosure/widget/inject_selective_disclosure_state.dart @@ -0,0 +1,34 @@ +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/selective_disclosure/widget/display_selective_disclosure.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ConsumeSelectiveDisclosureCubit extends StatelessWidget { + const ConsumeSelectiveDisclosureCubit({ + super.key, + required this.credentialModel, + required this.showVertically, + this.onPressed, + this.parentKeyId, + }); + + final CredentialModel credentialModel; + final bool showVertically; + final void Function(String?, String, String?)? onPressed; + + final String? parentKeyId; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return DisplaySelectiveDisclosure( + credentialModel: credentialModel, + selectiveDisclosureState: state, + onPressed: onPressed, + showVertically: true, + ); + }, + ); + } +} diff --git a/packages/credential_manifest/lib/src/models/field.dart b/packages/credential_manifest/lib/src/models/field.dart index e3693663a..1aa487a0f 100644 --- a/packages/credential_manifest/lib/src/models/field.dart +++ b/packages/credential_manifest/lib/src/models/field.dart @@ -15,6 +15,13 @@ class Field { final List path; final Filter? filter; + + // The fields object MAY contain an optional property. + // The value of this property MUST be a boolean, wherein true indicates + // the field is optional, and false or non-presence of the property indicates + // the field is required. Even when the optional property is present, + // the value located at the indicated path of the field MUST validate against + // the JSON Schema filter, if a filter is present. @JsonKey(defaultValue: false) final bool optional;