Skip to content

Commit

Permalink
feat(app): Add support for custom scope in verfication flow
Browse files Browse the repository at this point in the history
Signed-off-by: Talwinder kaur <[email protected]>
  • Loading branch information
Talwinder kaur committed Nov 14, 2023
1 parent 777bee3 commit c8a4e21
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 34 deletions.
32 changes: 27 additions & 5 deletions demo/app/ios/Runner/OpenID4VP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,25 +58,47 @@ public class OpenID4VP {
/**
* initiatedInteraction has PresentCredential method which presents credentials to redirect uri from request object.
*/
func presentCredential(selectedCredentials: VerifiableCredentialsArray) throws {
func presentCredential(selectedCredentials: VerifiableCredentialsArray, customScopes: Dictionary<String, Any>) throws {
// guard let vpQueryContent = self.vpQueryContent else {
// throw OpenID4VPError.runtimeError("OpenID4VP interaction not properly initialized, call processAuthorizationRequest first")
// }
guard let initiatedInteraction = self.initiatedInteraction else {
throw OpenID4VPError.runtimeError("OpenID4VP interaction not properly initialized, call processAuthorizationRequest first")
}

let opts = Openid4vpNewPresentCredentialOpts()

// let verifiablePresentation = try CredentialNewInquirer(documentLoader)!.query(vpQueryContent, credentials: selectedCredentials)

for scope in customScopes {
opts?.addScopeClaim(scope.key, claimJSON: scope.value as? String)

try initiatedInteraction.presentCredential(selectedCredentials)
}
try initiatedInteraction.presentCredentialOpts(selectedCredentials, opts: opts)

}

func getCustomScope() throws -> [String] {
guard let initiatedInteraction = self.initiatedInteraction else {
throw OpenID4VPError.runtimeError("OpenID4VP interaction not properly initialized, call processAuthorizationRequest first")
}

let customScopes = initiatedInteraction.customScope()
var customScopesList = [String]()

for i in 0...((customScopes?.length() ?? 0)-1) {
if (customScopes?.atIndex(i) != "openid"){
customScopesList.append(customScopes?.atIndex(i) ?? "")
}
}
// Otherwise return the default scope
return customScopesList
}

func getVerifierDisplayData() throws -> Openid4vpVerifierDisplayData {
guard self.initiatedInteraction != nil else {
throw OpenID4VPError.runtimeError("OpenID4VP interaction not properly initialized, call processAuthorizationRequest first")
}

let verifierDisplayData = try initiatedInteraction?.verifierDisplayData()
return verifierDisplayData!
return initiatedInteraction?.verifierDisplayData() ?? Openid4vpVerifierDisplayData()
}
}
30 changes: 24 additions & 6 deletions demo/app/ios/Runner/flutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public class SwiftWalletSDKPlugin: NSObject, FlutterPlugin {

case "getMatchedSubmissionRequirements":
getMatchedSubmissionRequirements(arguments: arguments!, result: result)

case "getCustomScope":
getCustomScope(result: result)

case "presentCredential":
presentCredential(arguments: arguments!, result: result)
Expand Down Expand Up @@ -247,6 +250,23 @@ public class SwiftWalletSDKPlugin: NSObject, FlutterPlugin {
}
}

public func getCustomScope(result: @escaping FlutterResult){
do {
guard let openID4VP = self.openID4VP else{
return result(FlutterError.init(code: "NATIVE_ERR",
message: "error while process present credential",
details: "OpenID4VP interaction is not initialted"))
}

let customScopeList = try openID4VP.getCustomScope()
result(customScopeList)
} catch let error as NSError{
result(FlutterError.init(code: "NATIVE_ERR",
message: "error while getting custom scope ",
details: error.localizedDescription))
}
}

/**
This method invokes presentCredentialt defined in OpenID4Vp file.
*/
Expand All @@ -259,6 +279,8 @@ public class SwiftWalletSDKPlugin: NSObject, FlutterPlugin {
}

let selectedCredentials = arguments["selectedCredentials"] as? Array<String>
let customScopeList = arguments["customScopeList"] as? Dictionary<String, Any>


let selectedCredentialsArray: VerifiableCredentialsArray?
if (selectedCredentials != nil) {
Expand All @@ -274,14 +296,10 @@ public class SwiftWalletSDKPlugin: NSObject, FlutterPlugin {
}

try openID4VP.presentCredential(
selectedCredentials: selectedCredentialsArray!)
selectedCredentials: selectedCredentialsArray!, customScopes: customScopeList!)
result(true);

} catch OpenID4VPError.runtimeError(let errorMsg as NSError){
result(FlutterError.init(code: "NATIVE_ERR",
message: "error while processing present credential",
details: errorMsg.localizedDescription))
} catch let error as NSError{
} catch let error as NSError{
result(FlutterError.init(code: "NATIVE_ERR",
message: "error while processing present credential",
details: error.localizedDescription))
Expand Down
6 changes: 6 additions & 0 deletions demo/app/lib/assets/customScopes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"test": {
"email": "[email protected]",
"name": "Test User"
}
}
16 changes: 16 additions & 0 deletions demo/app/lib/services/config_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,20 @@ class ConfigService {
log('decodedResponse $configResponseDecoded');
return connectIssuerConfigList;
}

Future<Map<String, dynamic>> readCustomScopeConfig(List<Object?> customScopeList) async {
Map<String, dynamic> customScopeConfigList = {};
final String scopeConfigResponse = await rootBundle.loadString('lib/assets/customScopes.json');
final configDataResp = await json.decode(scopeConfigResponse);

List<String> customScopeStr = customScopeList.cast<String>();
for (var customScope in customScopeStr) {
var claimJSON = configDataResp[customScope];
customScopeConfigList.addAll(
{customScope.toString(): jsonEncode(claimJSON)}
);
}
log('customScope config list $customScopeConfigList');
return customScopeConfigList;
}
}
35 changes: 21 additions & 14 deletions demo/app/lib/views/presentation_preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import 'package:app/models/activity_data_object.dart';
import 'package:app/widgets/credential_card.dart';
import 'package:app/views/custom_error.dart';

import 'package:app/services/config_service.dart';

class PresentationPreview extends StatefulWidget {
final CredentialData credentialData;

Expand Down Expand Up @@ -160,20 +162,25 @@ class PresentationPreviewState extends State<PresentationPreview> {
PrimaryButton(
onPressed: () async {
final SharedPreferences pref = await prefs;
try {
await WalletSDKPlugin.presentCredential(
selectedCredentials: [widget.credentialData.rawCredential]);
} catch (error) {
var errString = error.toString().replaceAll(r'\', '');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomError(
titleBar: 'Presentation Preview',
requestErrorTitleMsg: 'error while presenting credential',
requestErrorSubTitleMsg: errString)));
return;
}
Map<String, dynamic> customScopeConfigList = {};
final ConfigService configService = ConfigService();
WalletSDKPlugin.getCustomScope()
.then((customScopeList) async {
customScopeConfigList = await configService.readCustomScopeConfig(customScopeList);
})
.whenComplete(() => WalletSDKPlugin.presentCredential(
selectedCredentials: [widget.credentialData.rawCredential],
customScopeList: customScopeConfigList))
.onError((error, stackTrace) {
var errString = error.toString().replaceAll(r'\', '');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomError(
titleBar: 'Presentation Preview',
requestErrorTitleMsg: 'error while presenting credential',
requestErrorSubTitleMsg: errString)));
});
var activities = await WalletSDKPlugin.storeActivityLogger();
var credID = pref.getString('credID');
_storageService.addActivities(ActivityDataObj(credID!, activities));
Expand Down
24 changes: 22 additions & 2 deletions demo/app/lib/views/presentation_preview_multi_cred_radio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import 'package:app/services/storage_service.dart';
import 'package:app/models/activity_data_object.dart';
import 'package:app/widgets/credential_card.dart';

import '../services/config_service.dart';
import 'custom_error.dart';

class PresentationPreviewMultiCred extends StatefulWidget {
final List<CredentialData> credentialData;

Expand Down Expand Up @@ -174,8 +177,25 @@ class PresentationPreviewMultiCredState extends State<PresentationPreviewMultiCr
PrimaryButton(
onPressed: () async {
final SharedPreferences pref = await prefs;
await WalletSDKPlugin.presentCredential(
selectedCredentials: [selectedCredentialData.rawCredential]);
Map<String, dynamic> customScopeConfigList = {};
final ConfigService configService = ConfigService();
WalletSDKPlugin.getCustomScope()
.then((customScopeList) async {
customScopeConfigList = await configService.readCustomScopeConfig(customScopeList);
})
.whenComplete(() => WalletSDKPlugin.presentCredential(
selectedCredentials: [selectedCredentialData.rawCredential],
customScopeList: customScopeConfigList))
.onError((error, stackTrace) {
var errString = error.toString().replaceAll(r'\', '');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomError(
titleBar: 'Presentation Preview',
requestErrorTitleMsg: 'error while presenting credential',
requestErrorSubTitleMsg: errString)));
});
var activities = await WalletSDKPlugin.storeActivityLogger();
var credID = pref.getString('credID');
_storageService.addActivities(ActivityDataObj(credID!, activities));
Expand Down
26 changes: 19 additions & 7 deletions demo/app/lib/wallet_sdk/wallet_sdk_mobile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ class WalletSDK extends WalletPlatform {
}
}

Future<List<SupportedCredentials>> initializeWalletInitiatedFlow(String issuerURI, List<String> credentialTypes ) async {
Future<List<SupportedCredentials>> initializeWalletInitiatedFlow(
String issuerURI, List<String> credentialTypes) async {
try {
List<dynamic> supportedCredentialResp =
await methodChannel.invokeMethod('initializeWalletInitiatedFlow', <String, dynamic>{'issuerURI': issuerURI, 'credentialTypes': credentialTypes});
List<dynamic> supportedCredentialResp = await methodChannel.invokeMethod('initializeWalletInitiatedFlow',
<String, dynamic>{'issuerURI': issuerURI, 'credentialTypes': credentialTypes});
return supportedCredentialResp.map((d) => SupportedCredentials.fromMap(d.cast<String, dynamic>())).toList();
} on PlatformException catch (error) {
debugPrint(error.toString());
Expand Down Expand Up @@ -156,6 +157,10 @@ class WalletSDK extends WalletPlatform {
.toList();
}

Future<List<Object?>> getCustomScope() async {
return await methodChannel.invokeMethod('getCustomScope');
}

Future<List<SubmissionRequirement>> getSubmissionRequirements({required List<String>? storedCredentials}) async {
return (await methodChannel.invokeMethod<List<dynamic>>(
'getMatchedSubmissionRequirements', <String, dynamic>{'storedCredentials': storedCredentials}))!
Expand Down Expand Up @@ -185,9 +190,15 @@ class WalletSDK extends WalletPlatform {
purpose: data['purpose'] as String);
}

Future<void> presentCredential({required List<String> selectedCredentials}) async {
await methodChannel
.invokeMethod('presentCredential', <String, dynamic>{'selectedCredentials': selectedCredentials});
Future<void> presentCredential(
{required List<String> selectedCredentials, Map<String, dynamic>? customScopeList}) async {
try {
return await methodChannel.invokeMethod('presentCredential',
<String, dynamic>{'selectedCredentials': selectedCredentials, 'customScopeList': customScopeList});
} on PlatformException catch (error) {
debugPrint(error.toString());
rethrow;
}
}

Future<List<Object?>> storeActivityLogger() async {
Expand All @@ -212,7 +223,8 @@ class WalletSDK extends WalletPlatform {
}

Future<List<IssuerMetaData>> getIssuerMetaData(List<String> credentialTypes) async {
List<dynamic> getIssuerMetaDataResp = await methodChannel.invokeMethod('getIssuerMetaData', <String, dynamic>{'credentialTypes': credentialTypes});
List<dynamic> getIssuerMetaDataResp =
await methodChannel.invokeMethod('getIssuerMetaData', <String, dynamic>{'credentialTypes': credentialTypes});
return getIssuerMetaDataResp.map((d) => IssuerMetaData.fromMap(d.cast<String, dynamic>())).toList();
}

Expand Down
1 change: 1 addition & 0 deletions demo/app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ flutter:
- lib/assets/images/logoIcon.png
- lib/assets/issuerAuthFlowConfig.json
- lib/assets/walletInitiatedIssuerConfig.json
- lib/assets/customScopes.json

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
Expand Down

0 comments on commit c8a4e21

Please sign in to comment.