From 20afacc2d625dd6203aac7ceb6c64c89c8cf68d3 Mon Sep 17 00:00:00 2001 From: Bibash Shrestha Date: Wed, 28 Feb 2024 17:52:21 +0545 Subject: [PATCH] show token endpoint in developer mode --- .../shared/enum/status/qr_scan_status.dart | 1 + .../helper_functions/helper_functions.dart | 6 +- .../cubit/qr_code_scan_cubit.dart | 105 ++++++++--- .../cubit/qr_code_scan_state.dart | 18 +- lib/oidc4vc/add_credential_data.dart | 96 ++++++++++ lib/oidc4vc/get_and_add_credential.dart | 165 ------------------ lib/oidc4vc/get_credential.dart | 93 ++++++++++ lib/oidc4vc/oidc4vc.dart | 3 +- lib/splash/bloclisteners/blocklisteners.dart | 43 +++++ packages/oidc4vc/lib/src/oidc4vc.dart | 28 ++- 10 files changed, 355 insertions(+), 203 deletions(-) create mode 100644 lib/oidc4vc/add_credential_data.dart delete mode 100644 lib/oidc4vc/get_and_add_credential.dart create mode 100644 lib/oidc4vc/get_credential.dart diff --git a/lib/app/shared/enum/status/qr_scan_status.dart b/lib/app/shared/enum/status/qr_scan_status.dart index 22a7432a0..06d8365fa 100644 --- a/lib/app/shared/enum/status/qr_scan_status.dart +++ b/lib/app/shared/enum/status/qr_scan_status.dart @@ -8,4 +8,5 @@ enum QrScanStatus { error, success, goBack, + pauseForDialog, } diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index 441ee0b3c..274873f80 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -1176,12 +1176,12 @@ $codeForAuthorisedFlow } String showFormatted({ - required String title, - required Map jsonValue, + required String? title, + required Map? jsonValue, }) { return ''' $title : -${const JsonEncoder.withIndent(' ').convert(jsonValue)} +${jsonValue != null ? const JsonEncoder.withIndent(' ').convert(jsonValue) : 'None'} '''; } 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 a7f64e192..b9df3e442 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 @@ -1194,6 +1194,8 @@ class QRCodeScanCubit extends Cubit { } } + Completer? completer; + Future addCredentialsInLoop({ required List selectedCredentials, required bool isEBSIV3, @@ -1213,31 +1215,84 @@ class QRCodeScanCubit extends Cubit { final customOidc4vcProfile = profileCubit.state.model.profileSetting .selfSovereignIdentityOptions.customOidc4vcProfile; - await getAndAddCredential( - scannedResponse: state.uri.toString(), - credentialsCubit: credentialsCubit, - oidc4vc: oidc4vc, - isEBSIV3: isEBSIV3, - didKitProvider: didKitProvider, - secureStorageProvider: getSecureStorage, - credential: selectedCredentials[i], - isLastCall: i + 1 == selectedCredentials.length, - dioClient: client, - userPin: userPin, - issuer: issuer, - preAuthorizedCode: preAuthorizedCode, - codeForAuthorisedFlow: codeForAuthorisedFlow, - codeVerifier: codeVerifier, - cryptoHolderBinding: customOidc4vcProfile.cryptoHolderBinding, - authorization: authorization, - oidc4vciDraftType: customOidc4vcProfile.oidc4vciDraft, - didKeyType: customOidc4vcProfile.defaultDid, - clientId: clientId, - clientSecret: clientSecret, - profileCubit: profileCubit, - jwtDecode: jwtDecode, - blockchainType: walletCubit.state.currentAccount!.blockchainType, - ); + if (preAuthorizedCode != null || + (codeForAuthorisedFlow != null && codeVerifier != null)) { + /// codeForAuthorisedFlow != null + /// this is second phase flow for authorization_code + /// first phase is need for the authentication + /// + /// preAuthorizedCode != null + /// this is full phase flow for preAuthorizedCode + + /// get credentials + final ( + List encodedCredentialOrFutureTokens, + String? deferredCredentialEndpoint, + String format, + OpenIdConfiguration? openIdConfiguration, + Map? tokenResponse, + ) = await getCredential( + oidc4vc: oidc4vc, + isEBSIV3: isEBSIV3, + didKitProvider: didKitProvider, + credential: selectedCredentials[i], + userPin: userPin, + issuer: issuer, + preAuthorizedCode: preAuthorizedCode, + codeForAuthorisedFlow: codeForAuthorisedFlow, + codeVerifier: codeVerifier, + cryptoHolderBinding: customOidc4vcProfile.cryptoHolderBinding, + authorization: authorization, + oidc4vciDraftType: customOidc4vcProfile.oidc4vciDraft, + didKeyType: customOidc4vcProfile.defaultDid, + clientId: clientId, + clientSecret: clientSecret, + profileCubit: profileCubit, + ); + + if (profileCubit.state.model.isDeveloperMode && + tokenResponse != null) { + completer = Completer(); + emit( + state.copyWith( + qrScanStatus: QrScanStatus.pauseForDialog, + title: 'TOKEN_RESPONSE', + jsonValue: tokenResponse, + ), + ); + + final value = await completer!.future; + + if (value) { + completer = null; + } else { + completer = null; + oidc4vc.resetNonceAndAccessTokenAndAuthorizationDetails(); + goBack(); + return; + } + } + await addCredentialData( + scannedResponse: state.uri.toString(), + credentialsCubit: credentialsCubit, + secureStorageProvider: getSecureStorage, + credential: selectedCredentials[i], + isLastCall: i + 1 == selectedCredentials.length, + issuer: issuer, + profileCubit: profileCubit, + jwtDecode: jwtDecode, + blockchainType: walletCubit.state.currentAccount!.blockchainType, + deferredCredentialEndpoint: deferredCredentialEndpoint, + encodedCredentialOrFutureTokens: encodedCredentialOrFutureTokens, + format: format, + openIdConfiguration: openIdConfiguration, + tokenResponse: tokenResponse, + ); + + /// add credentials + } else { + throw Exception(); + } } oidc4vc.resetNonceAndAccessTokenAndAuthorizationDetails(); diff --git a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_state.dart b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_state.dart index 1308501d2..7d4981e79 100644 --- a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_state.dart +++ b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_state.dart @@ -8,6 +8,8 @@ class QRCodeScanState extends Equatable { this.route, this.isScan = false, this.message, + this.title, + this.jsonValue, }); factory QRCodeScanState.fromJson(Map json) => @@ -20,6 +22,8 @@ class QRCodeScanState extends Equatable { final bool isScan; final StateMessage? message; + final String? title; + final Map? jsonValue; Map toJson() => _$QRCodeScanStateToJson(this); @@ -54,6 +58,8 @@ class QRCodeScanState extends Equatable { Route? route, Uri? uri, bool? isScan, + String? title, + Map? jsonValue, }) { return QRCodeScanState( status: qrScanStatus, @@ -61,9 +67,19 @@ class QRCodeScanState extends Equatable { isScan: isScan ?? this.isScan, uri: uri ?? this.uri, route: route, // route should be cleared when one route is done + title: title, + jsonValue: jsonValue, ); } @override - List get props => [status, uri, route, isScan, message]; + List get props => [ + status, + uri, + route, + isScan, + message, + title, + jsonValue, + ]; } diff --git a/lib/oidc4vc/add_credential_data.dart b/lib/oidc4vc/add_credential_data.dart new file mode 100644 index 000000000..8970bca09 --- /dev/null +++ b/lib/oidc4vc/add_credential_data.dart @@ -0,0 +1,96 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/credentials/credentials.dart'; +import 'package:altme/dashboard/dashboard.dart'; + +import 'package:altme/oidc4vc/oidc4vc.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:secure_storage/secure_storage.dart'; +import 'package:uuid/uuid.dart'; + +Future addCredentialData({ + required List encodedCredentialOrFutureTokens, + required String? deferredCredentialEndpoint, + required String format, + required OpenIdConfiguration? openIdConfiguration, + required Map? tokenResponse, + required SecureStorageProvider secureStorageProvider, + required ProfileCubit profileCubit, + required CredentialsCubit credentialsCubit, + required String scannedResponse, + required dynamic credential, + required String issuer, + required bool isLastCall, + required JWTDecode jwtDecode, + required BlockchainType blockchainType, +}) async { + for (int i = 0; i < encodedCredentialOrFutureTokens.length; i++) { + final data = encodedCredentialOrFutureTokens[i]; + final String credentialName = getCredentialData(credential); + final acceptanceToken = data['acceptance_token']; + + if (acceptanceToken != null && deferredCredentialEndpoint != null) { + /// add deferred card + final id = const Uuid().v4(); + + final credentialModel = CredentialModel( + id: id, + credentialPreview: Credential( + 'dummy1', + ['dummy2'], + [credentialName], + 'dummy4', + 'dummy5', + '', + [Proof.dummy()], + CredentialSubjectModel( + id: 'dummy7', + type: 'dummy8', + issuedBy: const Author(''), + credentialCategory: CredentialCategory.pendingCards, + credentialSubjectType: CredentialSubjectType.defaultCredential, + ), + [Translation('en', '')], + [Translation('en', '')], + CredentialStatusField.emptyCredentialStatusField(), + [Evidence.emptyEvidence()], + ), + data: const {}, + jwt: null, + format: 'jwt_vc', + image: '', + shareLink: '', + pendingInfo: PendingInfo( + acceptanceToken: acceptanceToken.toString(), + deferredCredentialEndpoint: deferredCredentialEndpoint, + format: format, + url: scannedResponse, + issuer: issuer, + requestedAt: DateTime.now(), + ), + ); + // insert the credential in the wallet + await credentialsCubit.insertCredential( + credential: credentialModel, + showStatus: false, + showMessage: + isLastCall && i + 1 == encodedCredentialOrFutureTokens.length, + isPendingCredential: true, + blockchainType: blockchainType, + ); + } else { + await addOIDC4VCCredential( + encodedCredentialFromOIDC4VC: data, + credentialsCubit: credentialsCubit, + issuer: issuer, + credentialType: credentialName, + isLastCall: + isLastCall && i + 1 == encodedCredentialOrFutureTokens.length, + format: format, + openIdConfiguration: openIdConfiguration, + jwtDecode: jwtDecode, + blockchainType: blockchainType, + ); + } + } +} diff --git a/lib/oidc4vc/get_and_add_credential.dart b/lib/oidc4vc/get_and_add_credential.dart deleted file mode 100644 index f91bbef2f..000000000 --- a/lib/oidc4vc/get_and_add_credential.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'package:altme/app/app.dart'; -import 'package:altme/credentials/credentials.dart'; -import 'package:altme/dashboard/dashboard.dart'; - -import 'package:altme/oidc4vc/oidc4vc.dart'; -import 'package:did_kit/did_kit.dart'; -import 'package:jwt_decode/jwt_decode.dart'; -import 'package:oidc4vc/oidc4vc.dart'; -import 'package:secure_storage/secure_storage.dart'; -import 'package:uuid/uuid.dart'; - -Future getAndAddCredential({ - required String scannedResponse, - required OIDC4VC oidc4vc, - required bool isEBSIV3, - required DIDKitProvider didKitProvider, - required CredentialsCubit credentialsCubit, - required dynamic credential, - required SecureStorageProvider secureStorageProvider, - required ProfileCubit profileCubit, - required bool isLastCall, - required DioClient dioClient, - required String? userPin, - required String? preAuthorizedCode, - required String issuer, - required String? codeForAuthorisedFlow, - required String? codeVerifier, - required bool cryptoHolderBinding, - required String? authorization, - required OIDC4VCIDraftType oidc4vciDraftType, - required DidKeyType didKeyType, - required String? clientId, - required String? clientSecret, - required JWTDecode jwtDecode, - required BlockchainType blockchainType, -}) async { - final privateKey = await fetchPrivateKey( - isEBSIV3: isEBSIV3, - oidc4vc: oidc4vc, - secureStorage: getSecureStorage, - didKeyType: didKeyType, - ); - - final (did, kid) = await fetchDidAndKid( - isEBSIV3: isEBSIV3, - privateKey: privateKey, - didKitProvider: didKitProvider, - secureStorage: getSecureStorage, - didKeyType: didKeyType, - ); - - if (preAuthorizedCode != null || - (codeForAuthorisedFlow != null && codeVerifier != null)) { - /// codeForAuthorisedFlow != null - /// this is second phase flow for authorization_code - /// first phase is need for the authentication - /// - /// preAuthorizedCode != null - /// this is full phase flow for preAuthorizedCode - - final customOidc4vcProfile = profileCubit.state.model.profileSetting - .selfSovereignIdentityOptions.customOidc4vcProfile; - - final index = getIndexValue(isEBSIV3: isEBSIV3, didKeyType: didKeyType); - - final ( - List encodedCredentialOrFutureTokens, - String? deferredCredentialEndpoint, - String format, - OpenIdConfiguration? openIdConfiguration, - ) = await oidc4vc.getCredential( - preAuthorizedCode: preAuthorizedCode, - issuer: issuer, - credential: credential, - did: did, - kid: kid, - clientId: clientId, - clientSecret: clientSecret, - privateKey: privateKey, - indexValue: index, - userPin: userPin, - code: codeForAuthorisedFlow, - codeVerifier: codeVerifier, - cryptoHolderBinding: cryptoHolderBinding, - authorization: authorization, - oidc4vciDraftType: oidc4vciDraftType, - clientType: customOidc4vcProfile.clientType, - proofHeaderType: customOidc4vcProfile.proofHeader, - clientAuthentication: customOidc4vcProfile.clientAuthentication, - redirectUri: Parameters.oidc4vcUniversalLink, - ); - - for (int i = 0; i < encodedCredentialOrFutureTokens.length; i++) { - final data = encodedCredentialOrFutureTokens[i]; - final String credentialName = getCredentialData(credential); - final acceptanceToken = data['acceptance_token']; - - if (acceptanceToken != null && deferredCredentialEndpoint != null) { - /// add deferred card - final id = const Uuid().v4(); - - final credentialModel = CredentialModel( - id: id, - credentialPreview: Credential( - 'dummy1', - ['dummy2'], - [credentialName], - 'dummy4', - 'dummy5', - '', - [Proof.dummy()], - CredentialSubjectModel( - id: 'dummy7', - type: 'dummy8', - issuedBy: const Author(''), - credentialCategory: CredentialCategory.pendingCards, - credentialSubjectType: CredentialSubjectType.defaultCredential, - ), - [Translation('en', '')], - [Translation('en', '')], - CredentialStatusField.emptyCredentialStatusField(), - [Evidence.emptyEvidence()], - ), - data: const {}, - jwt: null, - format: 'jwt_vc', - image: '', - shareLink: '', - pendingInfo: PendingInfo( - acceptanceToken: acceptanceToken.toString(), - deferredCredentialEndpoint: deferredCredentialEndpoint, - format: format, - url: scannedResponse, - issuer: issuer, - requestedAt: DateTime.now(), - ), - ); - // insert the credential in the wallet - await credentialsCubit.insertCredential( - credential: credentialModel, - showStatus: false, - showMessage: - isLastCall && i + 1 == encodedCredentialOrFutureTokens.length, - isPendingCredential: true, - blockchainType: blockchainType, - ); - } else { - await addOIDC4VCCredential( - encodedCredentialFromOIDC4VC: data, - credentialsCubit: credentialsCubit, - issuer: issuer, - credentialType: credentialName, - isLastCall: - isLastCall && i + 1 == encodedCredentialOrFutureTokens.length, - format: format, - openIdConfiguration: openIdConfiguration, - jwtDecode: jwtDecode, - blockchainType: blockchainType, - ); - } - } - } else { - throw Exception(); - } -} diff --git a/lib/oidc4vc/get_credential.dart b/lib/oidc4vc/get_credential.dart new file mode 100644 index 000000000..0073ff3d1 --- /dev/null +++ b/lib/oidc4vc/get_credential.dart @@ -0,0 +1,93 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/credentials/credentials.dart'; +import 'package:altme/dashboard/dashboard.dart'; + +import 'package:did_kit/did_kit.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:secure_storage/secure_storage.dart'; + +/// Retreive credential_type from url +/// credentialResponseData, deferredCredentialEndpoint, format, +/// openIdConfiguration, tokenResponse +Future< + ( + List, + String?, + String, + OpenIdConfiguration?, + Map? + )> getCredential({ + required OIDC4VC oidc4vc, + required bool isEBSIV3, + required DIDKitProvider didKitProvider, + required dynamic credential, + required ProfileCubit profileCubit, + required String? userPin, + required String? preAuthorizedCode, + required String issuer, + required String? codeForAuthorisedFlow, + required String? codeVerifier, + required bool cryptoHolderBinding, + required String? authorization, + required OIDC4VCIDraftType oidc4vciDraftType, + required DidKeyType didKeyType, + required String? clientId, + required String? clientSecret, +}) async { + final privateKey = await fetchPrivateKey( + isEBSIV3: isEBSIV3, + oidc4vc: oidc4vc, + secureStorage: getSecureStorage, + didKeyType: didKeyType, + ); + + final (did, kid) = await fetchDidAndKid( + isEBSIV3: isEBSIV3, + privateKey: privateKey, + didKitProvider: didKitProvider, + secureStorage: getSecureStorage, + didKeyType: didKeyType, + ); + + final customOidc4vcProfile = profileCubit.state.model.profileSetting + .selfSovereignIdentityOptions.customOidc4vcProfile; + + final index = getIndexValue(isEBSIV3: isEBSIV3, didKeyType: didKeyType); + + final ( + List encodedCredentialOrFutureTokens, + String? deferredCredentialEndpoint, + String format, + OpenIdConfiguration? openIdConfiguration, + Map? tokenResponse, + ) = await oidc4vc.getCredential( + preAuthorizedCode: preAuthorizedCode, + issuer: issuer, + credential: credential, + did: did, + kid: kid, + clientId: clientId, + clientSecret: clientSecret, + privateKey: privateKey, + indexValue: index, + userPin: userPin, + code: codeForAuthorisedFlow, + codeVerifier: codeVerifier, + cryptoHolderBinding: cryptoHolderBinding, + authorization: authorization, + oidc4vciDraftType: oidc4vciDraftType, + clientType: customOidc4vcProfile.clientType, + proofHeaderType: customOidc4vcProfile.proofHeader, + clientAuthentication: customOidc4vcProfile.clientAuthentication, + redirectUri: Parameters.oidc4vcUniversalLink, + ); + + return ( + encodedCredentialOrFutureTokens, + deferredCredentialEndpoint, + format, + openIdConfiguration, + tokenResponse, + ); +} diff --git a/lib/oidc4vc/oidc4vc.dart b/lib/oidc4vc/oidc4vc.dart index 87bd0424b..8b1c2e8cf 100644 --- a/lib/oidc4vc/oidc4vc.dart +++ b/lib/oidc4vc/oidc4vc.dart @@ -1,7 +1,8 @@ +export 'add_credential_data.dart'; export 'add_oidc4vc_credential.dart'; -export 'get_and_add_credential.dart'; export 'get_and_add_deffered_credential.dart'; export 'get_authorization_uri_for_issuer.dart'; +export 'get_credential.dart'; export 'get_credential_offer_json.dart'; export 'initiate_oidv4vc_credential_issuance.dart'; export 'verify_encoded_data.dart'; diff --git a/lib/splash/bloclisteners/blocklisteners.dart b/lib/splash/bloclisteners/blocklisteners.dart index 1799bbd52..73a0844ce 100644 --- a/lib/splash/bloclisteners/blocklisteners.dart +++ b/lib/splash/bloclisteners/blocklisteners.dart @@ -576,6 +576,49 @@ final qrCodeBlocListener = BlocListener( await context.read().completeSiopV2Flow(); } + if (state.status == QrScanStatus.pauseForDialog) { + LoadingView().hide(); + final formattedData = showFormatted( + title: state.title ?? '', + jsonValue: state.jsonValue, + ); + final bool moveAhead = await showDialog( + context: context, + builder: (_) { + return DeveloperModeDialog( + onDisplay: () { + Navigator.of(context).push( + JsonViewerPage.route( + title: l10n.display, + data: formattedData, + ), + ); + return; + }, + onDownload: () { + final box = context.findRenderObject() as RenderBox?; + final subject = l10n.shareWith; + + Share.share( + formattedData, + subject: subject, + sharePositionOrigin: + box!.localToGlobal(Offset.zero) & box.size, + ); + return; + }, + onSkip: () { + Navigator.of(context).pop(true); + }, + ); + }, + ) ?? + true; + + context.read().completer!.complete(moveAhead); + LoadingView().show(context: context); + } + if (state.status == QrScanStatus.success) { if (state.route != null) { await Navigator.of(context).push(state.route!); diff --git a/packages/oidc4vc/lib/src/oidc4vc.dart b/packages/oidc4vc/lib/src/oidc4vc.dart index a94ac24e0..880b6d3b1 100644 --- a/packages/oidc4vc/lib/src/oidc4vc.dart +++ b/packages/oidc4vc/lib/src/oidc4vc.dart @@ -386,7 +386,16 @@ class OIDC4VC { List? authorizationDetails; /// Retreive credential_type from url - Future<(List, String?, String, OpenIdConfiguration?)> getCredential({ + /// credentialResponseData, deferredCredentialEndpoint, format, + /// openIdConfiguration, tokenResponse + Future< + ( + List, + String?, + String, + OpenIdConfiguration?, + Map? + )> getCredential({ required String issuer, required dynamic credential, required String did, @@ -419,6 +428,8 @@ class OIDC4VC { oidc4vciDraftType: oidc4vciDraftType, ); + Map? tokenResponse; + if (accessToken == null) { final tokenData = buildTokenData( preAuthorizedCode: preAuthorizedCode, @@ -431,19 +442,19 @@ class OIDC4VC { redirectUri: redirectUri, ); - final response = await getToken( + tokenResponse = await getToken( tokenEndPoint: tokenEndPoint, tokenData: tokenData, authorization: authorization, ); - if (response is Map && response.containsKey('c_nonce')) { - cnonce = response['c_nonce'] as String; + if (tokenResponse.containsKey('c_nonce')) { + cnonce = tokenResponse['c_nonce'] as String; } - accessToken = response['access_token'] as String; + accessToken = tokenResponse['access_token'] as String; authorizationDetails = - response['authorization_details'] as List?; + tokenResponse['authorization_details'] as List?; } final issuerTokenParameters = IssuerTokenParameters( @@ -533,6 +544,7 @@ class OIDC4VC { deferredCredentialEndpoint, format, openIdConfiguration, + tokenResponse, ); } @@ -1113,7 +1125,7 @@ class OIDC4VC { } @visibleForTesting - Future getToken({ + Future> getToken({ required String tokenEndPoint, required Map tokenData, required String? authorization, @@ -1132,7 +1144,7 @@ class OIDC4VC { options: Options(headers: tokenHeaders), data: tokenData, ); - return tokenResponse.data; + return tokenResponse.data as Map; } Future extractVpToken({