Skip to content

Commit

Permalink
Making credential offer url handling more robust.
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Sorotokin <[email protected]>
  • Loading branch information
sorotokin authored and kdeus committed Nov 20, 2024
1 parent 5366a31 commit 18d2b96
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class AuthorizeServlet : BaseServlet() {
override fun doPost(req: HttpServletRequest, resp: HttpServletResponse) {
val code = req.getParameter("authorizationCode")
val pidData = req.getParameter("pidData")
val extraInfo = req.getParameter("extraInfo")
val id = codeToId(OpaqueIdType.AUTHORIZATION_STATE, code)
val storage = environment.getInterface(Storage::class)!!
val baseUri = URI(this.baseUrl)
Expand Down Expand Up @@ -130,7 +129,6 @@ class AuthorizeServlet : BaseServlet() {
}
}

data.putEntry("com.android.identity.server.openid4vci", "extraInfo", Cbor.encode(Tstr(extraInfo)))
state.credentialData = data.build()
storage.update("IssuanceState", "", id, ByteString(state.toCbor()))
}
Expand Down
2 changes: 0 additions & 2 deletions server/src/main/resources/resources/openid4vci/authorize.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ <h2>(mDoc format only currently)</h2>
<input type="hidden" name="authorizationCode" value="$authorizationCode"/>
<input type="hidden" name="pidData" id="pidData" value=""/>
<button id="verify" data-code="$pidReadingCode">Verify PID</button><br/>
<label for="extraInfo">Extra info (optional):</label>
<input type="text" id="extraInfo" name="extraInfo">
</form>
<script src="authorize.js"></script>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
application = getApplication() as WalletApplication
provisioningViewModel.init(
walletServerProvider = application.walletServerProvider,
documentStore = application.documentStore,
settingsModel = application.settingsModel
)
permissionTracker.updatePermissions()
// handle intents with schema openid-credential-offer://
handleOid4vciCredentialOfferIntent(intent)
Expand All @@ -106,7 +111,6 @@ class MainActivity : FragmentActivity() {
application = application,
provisioningViewModel = provisioningViewModel,
permissionTracker = permissionTracker,
sharedPreferences = application.sharedPreferences,
qrEngagementViewModel = qrEngagementViewModel,
documentModel = application.documentModel,
readerModel = application.readerModel,
Expand Down Expand Up @@ -143,14 +147,11 @@ class MainActivity : FragmentActivity() {
val query = getUrlQueryFromCustomSchemeUrl(intent.dataString!!)
val offer = extractCredentialIssuerData(query)
if (offer != null) {
routeRequest.value = WalletDestination.ProvisionDocument.route
provisioningViewModel.start(
walletServerProvider = application.walletServerProvider,
documentStore = application.documentStore,
settingsModel = application.settingsModel,
issuerIdentifier = null,
openid4VciCredentialOffer = offer
)
routeRequest.value = WalletDestination.ProvisionDocument.route
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import com.android.identity.issuance.DocumentExtensions.documentIdentifier
import com.android.identity.issuance.DocumentExtensions.issuingAuthorityConfiguration
import com.android.identity.issuance.DocumentExtensions.issuingAuthorityIdentifier
import com.android.identity.issuance.DocumentExtensions.refreshState
import com.android.identity.issuance.IssuingAuthority
import com.android.identity.issuance.ProofingFlow
import com.android.identity.issuance.RegistrationResponse
import com.android.identity.issuance.evidence.EvidenceRequest
Expand All @@ -27,11 +26,13 @@ import com.android.identity.issuance.remote.WalletServerProvider
import com.android.identity.util.Logger
import com.android.identity.util.fromBase64Url
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import kotlinx.io.bytestring.buildByteString
import org.json.JSONObject
import kotlin.coroutines.cancellation.CancellationException

class ProvisioningViewModel : ViewModel() {

Expand All @@ -52,9 +53,21 @@ class ProvisioningViewModel : ViewModel() {

var error: Throwable? = null

private lateinit var issuer: IssuingAuthority
private lateinit var walletServerProvider: WalletServerProvider
private lateinit var documentStore: DocumentStore
private lateinit var settingsModel: SettingsModel

var openid4VciCredentialOffer: Openid4VciCredentialOffer? = null
private var openid4VciCredentialOffer: Openid4VciCredentialOffer? = null

fun init(
walletServerProvider: WalletServerProvider,
documentStore: DocumentStore,
settingsModel: SettingsModel,
) {
this.walletServerProvider = walletServerProvider
this.documentStore = documentStore
this.settingsModel = settingsModel
}

fun reset() {
state.value = State.IDLE
Expand All @@ -65,46 +78,46 @@ class ProvisioningViewModel : ViewModel() {
currentEvidenceRequestIndex = 0
nextEvidenceRequest.value = null
selectedOpenid4VpCredential.value = null
documentStore = null
settingsModel = null
}

private var proofingFlow: ProofingFlow? = null

var document: Document? = null
private var evidenceRequests: List<EvidenceRequest>? = null
private var currentEvidenceRequestIndex: Int = 0
private var documentStore: DocumentStore? = null
private var settingsModel: SettingsModel? = null
private var job: Job? = null

val nextEvidenceRequest = mutableStateOf<EvidenceRequest?>(null)
val selectedOpenid4VpCredential = mutableStateOf<Credential?>(null)

fun start(
walletServerProvider: WalletServerProvider,
documentStore: DocumentStore,
settingsModel: SettingsModel,
// PID-based mdoc or sd-jwt
issuerIdentifier: String?,
openid4VciCredentialOffer: Openid4VciCredentialOffer? = null,
) {
this.documentStore = documentStore
this.settingsModel = settingsModel
this.openid4VciCredentialOffer = openid4VciCredentialOffer
viewModelScope.launch(Dispatchers.IO) {
val lastJob = this.job
if (lastJob != null) {
this.job = null
lastJob.cancel(CancellationException("New provisioning started"))
}
this.job = viewModelScope.launch(Dispatchers.IO) {
lastJob?.join()
reset()
this@ProvisioningViewModel.openid4VciCredentialOffer = openid4VciCredentialOffer
state.value = State.IDLE
try {
if (openid4VciCredentialOffer != null) {
issuer = walletServerProvider.createOpenid4VciIssuingAuthorityByUri(
val issuer = if (openid4VciCredentialOffer != null) {
walletServerProvider.createOpenid4VciIssuingAuthorityByUri(
openid4VciCredentialOffer.issuerUri,
openid4VciCredentialOffer.configurationId
)
} else {
issuer = walletServerProvider.getIssuingAuthority(issuerIdentifier!!)
walletServerProvider.getIssuingAuthority(issuerIdentifier!!)
}
val issuerConfiguration = issuer.getConfiguration()

state.value = State.CREDENTIAL_REGISTRATION
val createDocumentKeyFlow = this@ProvisioningViewModel.issuer.register()
val createDocumentKeyFlow = issuer.register()
val documentRegistrationConfiguration =
createDocumentKeyFlow.getDocumentRegistrationConfiguration()
val issuerDocumentIdentifier = documentRegistrationConfiguration.documentId
Expand Down Expand Up @@ -134,9 +147,7 @@ class ProvisioningViewModel : ViewModel() {

if (evidenceRequests!!.size == 0) {
state.value = State.PROOFING_COMPLETE
document!!.let {
it.refreshState(walletServerProvider)
}
document!!.refreshState(walletServerProvider)
documentStore.addDocument(document!!)
proofingFlow!!.complete()
} else {
Expand All @@ -156,10 +167,7 @@ class ProvisioningViewModel : ViewModel() {
}

fun evidenceCollectionFailed(
error: Throwable,
walletServerProvider: WalletServerProvider,
documentStore: DocumentStore
) {
error: Throwable ) {
if (document != null) {
documentStore.deleteDocument(document!!.name)
}
Expand All @@ -168,11 +176,8 @@ class ProvisioningViewModel : ViewModel() {
state.value = State.FAILED
}

fun provideEvidence(
evidence: EvidenceResponse,
walletServerProvider: WalletServerProvider,
) {
viewModelScope.launch(Dispatchers.IO) {
fun provideEvidence(evidence: EvidenceResponse) {
this.job = viewModelScope.launch(Dispatchers.IO) {
try {
state.value = State.SUBMITTING_EVIDENCE

Expand All @@ -185,7 +190,7 @@ class ProvisioningViewModel : ViewModel() {
if (evidenceRequests!!.isEmpty()) {
state.value = State.PROOFING_COMPLETE
document!!.refreshState(walletServerProvider)
documentStore!!.addDocument(document!!)
documentStore.addDocument(document!!)
proofingFlow!!.complete()
document!!.refreshState(walletServerProvider)
} else {
Expand All @@ -208,7 +213,7 @@ class ProvisioningViewModel : ViewModel() {
}
} catch (e: Throwable) {
if (document != null) {
documentStore!!.deleteDocument(document!!.name)
documentStore.deleteDocument(document!!.name)
}
Logger.w(TAG, "Error submitting evidence", e)
e.printStackTrace()
Expand Down Expand Up @@ -330,25 +335,25 @@ class ProvisioningViewModel : ViewModel() {
docType: String
): Document? {
// prefer the credential which is on-screen if possible
val credentialIdFromPager: String? = settingsModel!!.focusedCardId.value
val credentialIdFromPager: String? = settingsModel.focusedCardId.value
if (credentialIdFromPager != null
&& canDocumentSatisfyRequest(credentialIdFromPager, credentialFormat, docType)
) {
return documentStore!!.lookupDocument(credentialIdFromPager)
return documentStore.lookupDocument(credentialIdFromPager)
}

val docId = documentStore!!.listDocuments().firstOrNull { credentialId ->
val docId = documentStore.listDocuments().firstOrNull { credentialId ->
canDocumentSatisfyRequest(credentialId, credentialFormat, docType)
}
return docId?.let { documentStore!!.lookupDocument(it) }
return docId?.let { documentStore.lookupDocument(it) }
}

private fun canDocumentSatisfyRequest(
credentialId: String,
credentialFormat: CredentialFormat,
docType: String
): Boolean {
val document = documentStore!!.lookupDocument(credentialId) ?: return false
val document = documentStore.lookupDocument(credentialId) ?: return false
val documentConfiguration = document.documentConfiguration
return when (credentialFormat) {
CredentialFormat.MDOC_MSO -> documentConfiguration.mdocConfiguration?.docType == docType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.android.identity_credential.wallet.navigation

import android.content.SharedPreferences
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.NavHostController
Expand Down Expand Up @@ -65,13 +64,11 @@ fun WalletNavigation(
application: WalletApplication,
provisioningViewModel: ProvisioningViewModel,
permissionTracker: PermissionTracker,
sharedPreferences: SharedPreferences,
qrEngagementViewModel: QrEngagementViewModel,
documentModel: DocumentModel,
readerModel: ReaderModel,
) {
val onNavigate = { routeWithArgs: String -> navigateTo(navController, routeWithArgs) }
val credentialStore = application.documentStore
NavHost(
navController = navController,
startDestination = WalletDestination.Main.route
Expand Down Expand Up @@ -123,12 +120,9 @@ fun WalletNavigation(
*/
composable(WalletDestination.AddToWallet.route) {
AddToWalletScreen(
documentModel = documentModel,
provisioningViewModel = provisioningViewModel,
onNavigate = onNavigate,
documentStore = application.documentStore,
walletServerProvider = application.walletServerProvider,
settingsModel = application.settingsModel,
walletServerProvider = application.walletServerProvider
)
}

Expand Down Expand Up @@ -212,7 +206,6 @@ fun WalletNavigation(
onNavigate = onNavigate,
permissionTracker = permissionTracker,
walletServerProvider = application.walletServerProvider,
documentStore = application.documentStore,
developerMode = application.settingsModel.developerModeEnabled.value ?: false
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.identity.document.DocumentStore
import com.android.identity.issuance.IssuingAuthorityConfiguration
import com.android.identity.issuance.remote.WalletServerProvider
import com.android.identity.util.Logger
import com.android.identity_credential.wallet.DocumentModel
import com.android.identity_credential.wallet.ProvisioningViewModel
import com.android.identity_credential.wallet.R
import com.android.identity_credential.wallet.SettingsModel
import com.android.identity_credential.wallet.WalletApplication
import com.android.identity_credential.wallet.credentialoffer.extractCredentialIssuerData
import com.android.identity_credential.wallet.navigation.WalletDestination
Expand Down Expand Up @@ -76,12 +73,9 @@ private suspend fun getIssuerDisplayDatas(

@Composable
fun AddToWalletScreen(
documentModel: DocumentModel,
provisioningViewModel: ProvisioningViewModel,
onNavigate: (String) -> Unit,
documentStore: DocumentStore,
walletServerProvider: WalletServerProvider,
settingsModel: SettingsModel,
) {
val loadingIssuerDisplayDatas = remember { mutableStateOf(true) }
val loadingIssuerDisplayError = remember { mutableStateOf<Throwable?>(null) }
Expand Down Expand Up @@ -144,9 +138,6 @@ fun AddToWalletScreen(
if (offer != null) {
// initiate getting issuing authority dynamically from specified Issuer Uri and Credential Id
provisioningViewModel.start(
walletServerProvider = walletServerProvider,
settingsModel = settingsModel,
documentStore = documentStore,
issuerIdentifier = null,
openid4VciCredentialOffer = offer,
)
Expand All @@ -161,9 +152,6 @@ fun AddToWalletScreen(
AddToWalletScreenWithIssuerDisplayDatas(
provisioningViewModel,
onNavigate,
documentStore,
walletServerProvider,
settingsModel,
issuerDisplayDatas,
onShowScanQrDialog = {
showQrScannerDialog.value = true
Expand Down Expand Up @@ -221,9 +209,6 @@ private fun AddToWalletScreenLoading() {
private fun AddToWalletScreenWithIssuerDisplayDatas(
provisioningViewModel: ProvisioningViewModel,
onNavigate: (String) -> Unit,
documentStore: DocumentStore,
walletServerProvider: WalletServerProvider,
settingsModel: SettingsModel,
issuerDisplayDatas: SnapshotStateList<IssuerDisplayData>,
onShowScanQrDialog: () -> Unit,
) {
Expand All @@ -248,10 +233,7 @@ private fun AddToWalletScreenWithIssuerDisplayDatas(
onClick = {
provisioningViewModel.reset()
provisioningViewModel.start(
walletServerProvider = walletServerProvider,
documentStore = documentStore,
issuerIdentifier = issuerDisplayData.configuration.identifier,
settingsModel = settingsModel,
)
onNavigate(WalletDestination.ProvisionDocument.route)
}) {
Expand Down
Loading

0 comments on commit 18d2b96

Please sign in to comment.