Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use real widget uris for PFi #6

Merged
merged 1 commit into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Flutter Starter</string>
<string>DIDPay</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/features/account/account_did_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AccountDidPage extends HookConsumerWidget {
appBar: AppBar(title: const Text('My DID')),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Center(child: SelectableText(did))),
child: Center(child: SelectableText(did.uri))),
);
}
}
3 changes: 2 additions & 1 deletion frontend/lib/features/account/account_providers.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:tbdex/tbdex.dart';

final didProvider = Provider<String>((ref) => throw UnimplementedError());
final didProvider = Provider<Did>((ref) => throw UnimplementedError());
23 changes: 16 additions & 7 deletions frontend/lib/features/home/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_starter/features/pfis/pfi_providers.dart';
import 'package:flutter_starter/features/pfis/pfi_verification_page.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:tbdex/tbdex.dart';

class HomePage extends HookConsumerWidget {
const HomePage({super.key});
Expand All @@ -16,14 +17,22 @@ class HomePage extends HookConsumerWidget {
...pfis.map(
(pfi) => ListTile(
title: Text(pfi.name),
subtitle: Text(pfi.id),
subtitle: Text(pfi.didUri),
trailing: const Icon(Icons.chevron_right),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => PfiVerificationPage(pfi: pfi),
),
);
onTap: () async {
final result = await DidDht.resolve(pfi.didUri);
final widgetService = result.didDocument?.service
?.firstWhere((e) => e.type == 'kyc-widget');
if (widgetService?.serviceEndpoint != null) {
// ignore: use_build_context_synchronously
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => PfiVerificationPage(
widgetUri: widgetService!.serviceEndpoint,
),
),
);
}
},
),
)
Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/features/pfis/pfi.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
class Pfi {
final String id;
final String name;
final String widgetUrl;
final String didUri;

Pfi({
required this.id,
required this.name,
required this.widgetUrl,
required this.didUri,
});
}
10 changes: 7 additions & 3 deletions frontend/lib/features/pfis/pfi_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';

final pfisProvider = Provider<List<Pfi>>(
(ref) => [
Pfi(
id: 'prototype',
name: 'Prototype PFI',
didUri: 'did:dht:3x1hbjobt577amnoeoxcenqrbjicym5mgsx6c6zszisf1igfj51y',
),
Pfi(
id: 'africa',
name: 'Africa',
widgetUrl: 'https://tbd.website',
didUri: 'coming soon...',
),
Pfi(
id: 'mexico',
name: 'Mexico',
widgetUrl:
'https://robustdisastrousstartups.wesbillman.repl.co/?proof=moeisreal&callback_uri=didpay://wes',
didUri: 'coming soon...',
),
],
);
8 changes: 4 additions & 4 deletions frontend/lib/features/pfis/pfi_verification_page.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_starter/features/pfis/pfi.dart';

class PfiVerificationPage extends StatelessWidget {
final Pfi pfi;
final String widgetUri;

const PfiVerificationPage({required this.pfi, super.key});
const PfiVerificationPage({required this.widgetUri, super.key});

@override
Widget build(BuildContext context) {
final fullPath = '$widgetUri?proof=moegrammer&callback_uri=didpay://kyc';
final controller = WebViewController()
..setBackgroundColor(Theme.of(context).colorScheme.background)
..setNavigationDelegate(
Expand All @@ -28,7 +28,7 @@ class PfiVerificationPage extends StatelessWidget {
},
),
)
..loadRequest(Uri.parse(pfi.widgetUrl));
..loadRequest(Uri.parse(fullPath));

return Scaffold(
appBar: AppBar(title: const Text('PFI Verification')),
Expand Down
26 changes: 14 additions & 12 deletions frontend/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_starter/features/account/account_providers.dart';
import 'package:flutter_starter/features/app/app.dart';
import 'package:flutter_starter/services/secure_storage_key_manager.dart';
import 'package:flutter_starter/services/service_providers.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:tbdex/tbdex.dart';
Expand All @@ -14,8 +15,8 @@ void main() async {
aOptions: AndroidOptions(encryptedSharedPreferences: true),
);

final did = await getOrCreateDid(storage);

final keyManager = SecureStorageKeyManager(storage);
final did = await getOrCreateDid(keyManager, storage);
runApp(ProviderScope(
overrides: [
secureStorage.overrideWithValue(storage),
Expand All @@ -25,16 +26,17 @@ void main() async {
));
}

Future<String> getOrCreateDid(FlutterSecureStorage storage) async {
const didKey = 'did';
final did = await storage.read(key: didKey);
if (did != null) {
return did;
Future<Did> getOrCreateDid(
KeyManager keyManager,
FlutterSecureStorage storage,
) async {
const didUriKey = 'did.uri';
final didUri = await storage.read(key: didUriKey);
if (didUri != null) {
return DidJwk(uri: didUri, keyManager: keyManager);
}

final keyManager = InMemoryKeyManager();
final jwt = await DidJwk.create(keyManager: keyManager);
await storage.write(key: didKey, value: jwt.uri);

return jwt.uri;
final did = await DidJwk.create(keyManager: keyManager);
await storage.write(key: didUriKey, value: did.uri);
return did;
}
42 changes: 42 additions & 0 deletions frontend/lib/services/secure_storage_key_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:tbdex/tbdex.dart';

class SecureStorageKeyManager implements KeyManager {
final FlutterSecureStorage _storage;
SecureStorageKeyManager(this._storage);

@override
Future<String> generatePrivateKey(DsaName alg) async {
final privateKeyJwk = await DsaAlgorithms.generatePrivateKey(alg);
final thumbprint = privateKeyJwk.computeThumbprint();
await _storage.write(key: thumbprint, value: privateKeyJwk.toString());
return thumbprint;
}

@override
Future<Jwk> getPublicKey(String keyAlias) async {
final privateKeyJwkStr = await _storage.read(key: keyAlias);

if (privateKeyJwkStr == null) {
throw Exception('No key found with alias $keyAlias');
}

final privateKeyJwk = Jwk.fromJson(json.decode(privateKeyJwkStr));
return DsaAlgorithms.computePublicKey(privateKeyJwk);
}

@override
Future<Uint8List> sign(String keyAlias, Uint8List payload) async {
final privateKeyJwkStr = await _storage.read(key: keyAlias);

if (privateKeyJwkStr == null) {
throw Exception('No key found with alias $keyAlias');
}

final privateKeyJwk = Jwk.fromJson(json.decode(privateKeyJwkStr));
return DsaAlgorithms.sign(privateKeyJwk, payload);
}
}
30 changes: 27 additions & 3 deletions frontend/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
base32:
dependency: transitive
description:
name: base32
sha256: ddad4ebfedf93d4500818ed8e61443b734ffe7cf8a45c668c9b34ef6adde02e2
url: "https://pub.dev"
source: hosted
version: "2.1.3"
boolean_selector:
dependency: transitive
description:
Expand Down Expand Up @@ -41,6 +49,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
Expand Down Expand Up @@ -82,10 +98,10 @@ packages:
dependency: "direct main"
description:
name: flutter_hooks
sha256: "7c8db779c2d1010aa7f9ea3fbefe8f86524fcb87b69e8b0af31e1a4b55422dec"
sha256: "09f64db63fee3b2ab8b9038a1346be7d8986977fae3fec601275bf32455ccfc0"
url: "https://pub.dev"
source: hosted
version: "0.20.3"
version: "0.20.4"
flutter_lints:
dependency: "direct dev"
description:
Expand Down Expand Up @@ -301,6 +317,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.7"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.3"
riverpod:
dependency: transitive
description:
Expand Down Expand Up @@ -359,7 +383,7 @@ packages:
description:
path: "."
ref: main
resolved-ref: "7ba39caf5a950196abfe881a0ffee307aa79c2c2"
resolved-ref: "16e4c095efa420e770a0ee32663f1f52dcfce095"
url: "https://github.com/TBD54566975/tbdex-dart.git"
source: git
version: "0.1.0"
Expand Down