Skip to content

Commit 2aab11c

Browse files
authored
chore: Update to @mattrglobal/mobile-credential-holder-react-native v8.1.0 (#206)
* chore: Update to @mattrglobal/mobile-credential-holder-react-native v8.1.0 - Fix API method names - Remove unnecessary type annotations - Fix deep linking configuration issues - Update starter template with same improvements - Fix credential offer redirect issues feat: add AcceptOfferHandler and AcceptIndexPage components for handling credential offers via deeplink chore: update starter android plugin * fix: delete wrongly provided HolderProvider.tsx from starter
1 parent a75ddfc commit 2aab11c

File tree

19 files changed

+1754
-1514
lines changed

19 files changed

+1754
-1514
lines changed
Lines changed: 82 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,86 @@
11
import type { ConfigContext, ExpoConfig } from "expo/config";
22

33
export default ({ config }: ConfigContext): ExpoConfig => ({
4-
...config,
5-
name: "react-native-mdocs-holder-tutorial-sample-app",
6-
slug: "react-native-mdocs-holder-tutorial-sample-app",
7-
version: "1.0.0",
8-
// Online presentation - Step 1.1: Update application custom scheme.
9-
scheme: "mdoc-openid4vp",
10-
orientation: "portrait",
11-
icon: "./assets/icon.png",
12-
splash: {
13-
image: "./assets/splash.png",
14-
resizeMode: "contain",
15-
backgroundColor: "#ffffff",
16-
},
17-
ios: {
18-
// Update the bundle identifier
19-
bundleIdentifier: "",
20-
infoPlist: {
21-
// Add Face ID and Camera usage permissions
22-
NSFaceIDUsageDescription: "Face ID is used to secure your credentials.",
23-
NSCameraUsageDescription: "Camera is used to scan QR codes.",
24-
// Add Bluetooth permissions
25-
NSBluetoothAlwaysUsageDescription:
26-
"This app uses Bluetooth to communicate with verifiers or holders.",
27-
NSBluetoothPeripheralUsageDescription:
28-
"This app uses Bluetooth to communicate with verifiers or holders.",
29-
},
30-
config: {
31-
usesNonExemptEncryption: false,
32-
},
33-
},
34-
android: {
35-
package: "io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
36-
adaptiveIcon: {
37-
foregroundImage: "./assets/adaptive-icon.png",
38-
backgroundColor: "#ffffff",
39-
},
40-
permissions: ["CAMERA"],
41-
intentFilters: [
42-
{
43-
action: "VIEW",
44-
data: {
45-
scheme:
46-
"io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
47-
host: "credentials",
48-
pathPrefix: "/",
49-
},
50-
category: ["BROWSABLE", "DEFAULT"],
51-
},
52-
{
53-
action: "VIEW",
54-
data: {
55-
scheme:
56-
"io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
57-
host: "expo-development-client",
58-
pathPrefix: "/",
59-
},
60-
category: ["BROWSABLE", "DEFAULT"],
61-
},
62-
],
63-
},
64-
plugins: [
65-
"expo-router",
66-
[
67-
"expo-build-properties",
68-
{
69-
ios: {
70-
deploymentTarget: "15.1",
71-
},
72-
},
73-
],
74-
[
75-
"react-native-vision-camera",
76-
{
77-
cameraPermissionText: "$(PRODUCT_NAME) needs access to your Camera.",
78-
enableCodeScanner: true,
79-
},
80-
],
81-
// Configure the SDK plugins
82-
/*
83-
* withAndroidHolderSDK plugin handles the Android SDK setup according to
84-
* https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:platform-android
85-
*
86-
*/
87-
"./withRemoveAPNSCapability.js",
88-
[
89-
"./withAndroidHolderSDK.js",
90-
{
91-
mattrDomain: "credentials",
92-
mattrScheme:
93-
"io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
94-
},
95-
],
96-
97-
// You may not have the APNS Capability enabled in your profile.
98-
],
99-
experiments: {
100-
typedRoutes: true,
101-
},
4+
...config,
5+
name: "react-native-mdocs-holder-tutorial-sample-app",
6+
slug: "react-native-mdocs-holder-tutorial-sample-app",
7+
version: "1.0.0",
8+
// Online presentation - Step 1.1: Update application custom scheme.
9+
scheme: "mdoc-openid4vp",
10+
orientation: "portrait",
11+
icon: "./assets/icon.png",
12+
splash: {
13+
image: "./assets/splash.png",
14+
resizeMode: "contain",
15+
backgroundColor: "#ffffff",
16+
},
17+
ios: {
18+
// Update the bundle identifier
19+
bundleIdentifier:
20+
"io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
21+
infoPlist: {
22+
// Add Face ID and Camera usage permissions
23+
NSFaceIDUsageDescription: "Face ID is used to secure your credentials.",
24+
NSCameraUsageDescription: "Camera is used to scan QR codes.",
25+
// Add Bluetooth permissions
26+
NSBluetoothAlwaysUsageDescription:
27+
"This app uses Bluetooth to communicate with verifiers or holders.",
28+
NSBluetoothPeripheralUsageDescription:
29+
"This app uses Bluetooth to communicate with verifiers or holders.",
30+
},
31+
config: {
32+
usesNonExemptEncryption: false,
33+
},
34+
},
35+
android: {
36+
package: "io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
37+
adaptiveIcon: {
38+
foregroundImage: "./assets/adaptive-icon.png",
39+
backgroundColor: "#ffffff",
40+
},
41+
permissions: ["CAMERA"],
42+
// Add intent filter for handling credential offers via deep links
43+
intentFilters: [
44+
{
45+
action: "VIEW",
46+
data: {
47+
scheme:
48+
"io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp",
49+
host: "accept",
50+
pathPrefix: "/",
51+
},
52+
category: ["BROWSABLE", "DEFAULT"],
53+
},
54+
],
55+
},
56+
plugins: [
57+
"expo-router",
58+
[
59+
"expo-build-properties",
60+
{
61+
ios: {
62+
deploymentTarget: "15.1",
63+
},
64+
},
65+
],
66+
[
67+
"react-native-vision-camera",
68+
{
69+
cameraPermissionText: "$(PRODUCT_NAME) needs access to your Camera.",
70+
enableCodeScanner: true,
71+
},
72+
],
73+
// Configure the SDK plugins
74+
/*
75+
* withAndroidHolderSDK plugin handles the Android SDK setup according to
76+
* https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:register-local-maven-repository-android
77+
*
78+
*/
79+
"./withAndroidHolderSDK.js",
80+
// You may not have the APNS Capability enabled in your profile.
81+
"./withRemoveAPNSCapability.js",
82+
],
83+
experiments: {
84+
typedRoutes: true,
85+
},
10286
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { useLocalSearchParams, useRouter } from "expo-router";
2+
import { useEffect } from "react";
3+
import { ActivityIndicator, Text, View } from "react-native";
4+
5+
function base64UrlDecode(base64Url: string): string {
6+
// Convert base64url to base64
7+
const base64 =
8+
base64Url.replace(/-/g, "+").replace(/_/g, "/") +
9+
"==".slice(0, (4 - (base64Url.length % 4)) % 4);
10+
return atob(base64);
11+
}
12+
13+
export default function AcceptOfferHandler() {
14+
const { offer } = useLocalSearchParams<{ offer: string }>();
15+
const router = useRouter();
16+
17+
useEffect(() => {
18+
console.log("Offer param:", offer);
19+
20+
if (!offer) {
21+
console.log("No offer parameter found, redirecting to home");
22+
router.replace("/");
23+
return;
24+
}
25+
26+
try {
27+
// Decode the base64url-encoded offer
28+
const decoded = base64UrlDecode(offer);
29+
console.log("Decoded credential offer:", decoded);
30+
31+
if (decoded.startsWith("openid-credential-offer://")) {
32+
console.log(
33+
"Valid credential offer detected, navigating to claim-credential",
34+
);
35+
// Navigate to claim-credential with the decoded offer
36+
router.replace({
37+
pathname: "/claim-credential",
38+
params: { scannedValue: decoded },
39+
});
40+
} else {
41+
console.log(
42+
"Decoded content is not a valid credential offer:",
43+
decoded,
44+
);
45+
router.replace("/");
46+
}
47+
} catch (error) {
48+
console.log("Failed to decode offer:", error);
49+
}
50+
}, [offer, router]);
51+
52+
return (
53+
<View
54+
style={{
55+
flex: 1,
56+
justifyContent: "center",
57+
alignItems: "center",
58+
backgroundColor: "#fff",
59+
}}
60+
>
61+
<ActivityIndicator size="large" color="#007AFF" />
62+
<Text style={{ marginTop: 16, fontSize: 16, color: "#333" }}>
63+
Processing credential offer...
64+
</Text>
65+
</View>
66+
);
67+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Redirect } from "expo-router";
2+
3+
export default function AcceptIndexPage() {
4+
// No offer provided, redirect to home
5+
console.log("Accept page accessed without offer, redirecting to home");
6+
return <Redirect href="/" />;
7+
}

0 commit comments

Comments
 (0)