Skip to content
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: 2 additions & 0 deletions .github/workflows/ktlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ on:
push:
branches:
- main
- code-starter
paths:
- MultipazCodelab/Holder/**
pull_request:
branches:
- main
- code-starter
paths:
- MultipazCodelab/Holder/**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,19 @@
requests on the lock screen because there is no PII. For an app with real
user data it might be a privacy problem to show PII on the lock screen.
-->
<!-- TODO: Add this NdefService-->
<service
android:name=".NdefService"
android:exported="true"
android:label="@string/nfc_ndef_service_description"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
</intent-filter>

<meta-data
android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/nfc_ndef_service" />
</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ class MainActivity : FragmentActivity() {
/**
* Handle a link (either a app link, universal link, or custom URL schema link).
*/
//TODO: implement HandleUrl for Android
fun handleUrl(url: String) {

handleUrl(
url = url,
credentialOffers = credentialOffers,
provisioningModel = provisioningModel,
provisioningSupport = provisioningSupport,
)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
android:requireDeviceScreenOn="false"
tools:ignore="UnusedAttribute">

<!--TODO: implement nfc_ndef_service xml-->
</host-apdu-service>
<aid-group android:description="@string/nfc_ndef_service_aid_group_description" android:category="other">
<!-- NFC Type 4 Tag -->
<aid-filter android:name="D2760000850101"/>
</aid-group>

</host-apdu-service>
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package org.multipaz.samples.wallet.cmp
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import org.koin.compose.koinInject
import org.multipaz.provisioning.ProvisioningModel
import org.multipaz.samples.wallet.cmp.ui.HomeScreen
import org.multipaz.samples.wallet.cmp.ui.ProvisioningTestScreen
import org.multipaz.samples.wallet.cmp.util.DigitalCredentialsRegistrationManager
import org.multipaz.samples.wallet.cmp.util.ProvisioningSupport
import org.multipaz.util.Logger

Expand All @@ -21,9 +24,11 @@ fun UtopiaSampleApp(
credentialOffers: Channel<String>,
provisioningModel: ProvisioningModel = koinInject(),
provisioningSupport: ProvisioningSupport = koinInject(),
registrationManager: DigitalCredentialsRegistrationManager = koinInject(),
) {
MaterialTheme {
val navController = rememberNavController()
val coroutineScope = rememberCoroutineScope()

NavHost(
navController = navController,
Expand All @@ -36,11 +41,18 @@ fun UtopiaSampleApp(
composable("provisioning") {
Logger.i(TAG, "NavHost: Rendering 'provisioning' route")
ProvisioningTestScreen(
onNavigateToMain = { navController.navigate("main") },
onNavigateToMain = {
coroutineScope.launch {
registrationManager.refresh("return from provisioning screen")
}
navController.popBackStack("main", inclusive = false)
},
)
}
}

// TODO: implement DC registration refreshing

// Use the working pattern from identity-credential project
LaunchedEffect(true) {
while (true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,8 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.koin.dsl.module
import org.multipaz.crypto.Algorithm
import org.multipaz.crypto.X509Cert
import org.multipaz.digitalcredentials.DigitalCredentials
import org.multipaz.digitalcredentials.getDefault
import org.multipaz.document.DocumentStore
import org.multipaz.document.buildDocumentStore
import org.multipaz.documenttype.DocumentTypeRepository
import org.multipaz.documenttype.knowntypes.DrivingLicense
import org.multipaz.documenttype.knowntypes.Loyalty
import org.multipaz.presentment.model.PresentmentModel
import org.multipaz.presentment.model.PresentmentSource
import org.multipaz.presentment.model.SimplePresentmentSource
import org.multipaz.prompt.PromptModel
import org.multipaz.prompt.promptModelRequestConsent
import org.multipaz.prompt.promptModelSilentConsent
import org.multipaz.provisioning.DocumentProvisioningHandler
import org.multipaz.provisioning.ProvisioningModel
import org.multipaz.provisioning.openid4vci.OpenID4VCIBackend
Expand All @@ -30,58 +18,51 @@ import org.multipaz.samples.wallet.cmp.util.AppSettingsModel
import org.multipaz.samples.wallet.cmp.util.OpenID4VCILocalBackend
import org.multipaz.samples.wallet.cmp.util.ProvisioningSupport
import org.multipaz.samples.wallet.cmp.util.ProvisioningSupport.Companion.APP_LINK_BASE_URL
import org.multipaz.samples.wallet.cmp.util.TestAppUtils
import org.multipaz.samples.wallet.cmp.util.createWalletStorage
import org.multipaz.samples.wallet.cmp.util.shouldRegisterDigitalCredentialsInCommonModule
import org.multipaz.securearea.SecureArea
import org.multipaz.securearea.SecureAreaRepository
import org.multipaz.storage.Storage
import org.multipaz.trustmanagement.TrustManager
import org.multipaz.trustmanagement.TrustManagerLocal
import org.multipaz.trustmanagement.TrustMetadata
import org.multipaz.trustmanagement.TrustPointAlreadyExistsException
import org.multipaz.util.Logger
import org.multipaz.util.Platform
import utopiasample.composeapp.generated.resources.Res

val multipazModule = module {
//TODO: define Storage in Koin module
//TODO: define SecureArea in Koin module
//TODO: define SecureAreaRepository in Koin module
//TODO: define DocumentStore in Koin module
//TODO: define DocumentTypeRepository in Koin module
single<PromptModel> {
Platform.promptModel
}
single<HttpClient> {
HttpClient {
followRedirects = false
val multipazModule =
module {
// TODO: define Storage in Koin module
// TODO: define SecureArea in Koin module
// TODO: define SecureAreaRepository in Koin module
// TODO: define DocumentStore in Koin module
// TODO: define DocumentTypeRepository in Koin module
single<PromptModel> {
Platform.promptModel
}
}
single<AppSettingsModel> {
runBlocking {
AppSettingsModel.create(
storage = get(),
readOnly = false,
single<HttpClient> {
HttpClient {
followRedirects = false
}
}
single<AppSettingsModel> {
runBlocking {
AppSettingsModel.create(
storage = get(),
readOnly = false,
)
}
}
single<ProvisioningModel> {
val secureArea: SecureArea = get()
ProvisioningModel(
documentProvisioningHandler =
DocumentProvisioningHandler(
secureArea = secureArea,
documentStore = get(),
),
httpClient = get(),
promptModel = get(),
authorizationSecureArea = secureArea,
)
}
}
single<ProvisioningModel> {
val secureArea: SecureArea = get()
ProvisioningModel(
documentProvisioningHandler =
DocumentProvisioningHandler(
secureArea = secureArea,
documentStore = get(),
),
httpClient = get(),
promptModel = get(),
authorizationSecureArea = secureArea,
)
}
//TODO: define TrustManager in Koin module
// TODO: define TrustManager in Koin module

// TODO: define Digit
// alCredentialsRegistrationManager in Koin module

//TODO: define PresentmentSource in Koin module
// TODO: define PresentmentSource in Koin module

single<ProvisioningSupport> {
ProvisioningSupport(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,58 @@ private fun ShowQrButton(
) {
when (hasCredentials) {
true -> {
// TODO: show qr button when credentials are available
Button(onClick = {
val connectionMethods = mutableListOf<MdocConnectionMethod>()
val bleUuid = UUID.randomUUID()
if (bleCentralClientEnabled) {
connectionMethods.add(
MdocConnectionMethodBle(
supportsPeripheralServerMode = false,
supportsCentralClientMode = true,
peripheralServerModeUuid = null,
centralClientModeUuid = bleUuid,
),
)
}
if (blePeripheralServerEnabled) {
connectionMethods.add(
MdocConnectionMethodBle(
supportsPeripheralServerMode = true,
supportsCentralClientMode = false,
peripheralServerModeUuid = bleUuid,
centralClientModeUuid = null,
),
)
}
if (nfcDataTransferEnabled) {
connectionMethods.add(
MdocConnectionMethodNfc(
commandDataFieldMaxLength = 0xffff,
responseDataFieldMaxLength = 0x10000,
),
)
}
onGenerateQrCode(
MdocProximityQrSettings(
availableConnectionMethods = connectionMethods,
createTransportOptions =
MdocTransportOptions(
bleUseL2CAP = bleL2CapEnabled,
bleUseL2CAPInEngagement = bleL2CapInEngagementEnabled,
),
),
)
}) {
Text("Present mDL via QR")
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text =
"The mDL is also available\n" +
"via NFC engagement and W3C DC API\n" +
"(Android-only right now)",
textAlign = TextAlign.Center,
)
}

false -> {
Expand Down Expand Up @@ -281,7 +332,12 @@ private fun ShowQrCode(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(text = "Present QR code to mdoc reader")
//TODO: show QR code
Image(
modifier = Modifier.fillMaxWidth(),
bitmap = qrCodeBitmap,
contentDescription = null,
contentScale = ContentScale.FillWidth,
)
Button(onClick = { onReset() }) {
Text("Cancel")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@ fun HomeScreen(documentStore: DocumentStore = koinInject()) {
val tabs = listOf("Explore", "Account")
var hasCredentials by remember { mutableStateOf<Boolean?>(null) }

LaunchedEffect(Unit) {
val hasCred = documentStore.hasAnyUsableCredential()
hasCredentials = hasCred
Logger.i(TAG, "AccountScreen: hasAnyUsableCredential: $hasCred")
LaunchedEffect(documentStore) {
suspend fun refreshCredentials() {
val hasCred = documentStore.hasAnyUsableCredential()
hasCredentials = hasCred
Logger.i(TAG, "AccountScreen: hasAnyUsableCredential: $hasCred")
}

refreshCredentials()
documentStore.eventFlow.collect {
refreshCredentials()
}
}

Scaffold(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,26 @@ fun ProvisioningTestScreen(
}

else -> {
//TODO: update text depends on provisioningState
val text =
when (provisioningState) {
ProvisioningModel.Idle -> "Initializing..."
ProvisioningModel.Initial -> "Starting provisioning..."
ProvisioningModel.Connected -> "Connected to the back-end"
ProvisioningModel.ProcessingAuthorization -> "Processing authorization..."
ProvisioningModel.Authorized -> "Authorized"
ProvisioningModel.RequestingCredentials -> "Requesting credentials..."
ProvisioningModel.CredentialsIssued -> "Credentials issued"
is ProvisioningModel.Error -> throw IllegalStateException()
is ProvisioningModel.Authorizing -> throw IllegalStateException()
}
Text(
modifier =
Modifier
.align(Alignment.CenterHorizontally)
.padding(8.dp),
style = MaterialTheme.typography.titleLarge,
text = text,
)
}
}
}
Expand All @@ -111,7 +130,11 @@ private fun Authorize(
EVIDENCE_REQUEST_WEB_VIEW,
"Authorize: Rendering EvidenceRequestWebView for OAuth challenge",
)
//TODO: init EvidenceRequestWebView
EvidenceRequestWebView(
evidenceRequest = challenge,
provisioningModel = provisioningModel,
provisioningSupport = provisioningSupport,
)
}

is AuthorizationChallenge.SecretText -> TODO()
Expand Down Expand Up @@ -155,7 +178,9 @@ fun EvidenceRequestWebView(
EVIDENCE_REQUEST_WEB_VIEW,
"EvidenceRequestWebView LaunchedEffect invokedUrl: $invokedUrl",
)
//TODO: add provideAuthorizationResponse
provisioningModel.provideAuthorizationResponse(
AuthorizationResponse.OAuth(stableEvidenceRequest.id, invokedUrl),
)
}
val uriHandler = LocalUriHandler.current
LaunchedEffect(stableEvidenceRequest.url) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ class AppSettingsModel private constructor(
boundItems.forEach { it.resetValue() }
}

// TODO: use something like KSP to avoid having to repeat settings name three times..
//

private suspend fun init() {
bind(presentmentBleCentralClientModeEnabled, "presentmentBleCentralClientModeEnabled", false)
bind(presentmentBlePeripheralServerModeEnabled, "presentmentBlePeripheralServerModeEnabled", true)
Expand Down
Loading
Loading