Skip to content

Commit 72838b7

Browse files
author
Helen Qin
committed
More vci support
1 parent 8ca302a commit 72838b7

File tree

9 files changed

+373
-54
lines changed

9 files changed

+373
-54
lines changed

app/src/main/java/com/credman/cmwallet/CmWalletApplication.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class CmWalletApplication : Application() {
4949
}
5050

5151
const val TAG = "CmWalletApplication"
52+
53+
const val TEST_VCI_CLIENT_ID = "52480754053"
5254
}
5355

5456
private val registryManager = RegistryManager.create(this)

app/src/main/java/com/credman/cmwallet/createcred/CreateCredentialActivity.kt

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.Row
1919
import androidx.compose.foundation.layout.fillMaxWidth
2020
import androidx.compose.foundation.layout.height
2121
import androidx.compose.foundation.layout.padding
22+
import androidx.compose.foundation.lazy.LazyColumn
2223
import androidx.compose.material3.BottomSheetDefaults
2324
import androidx.compose.material3.Button
2425
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -126,7 +127,7 @@ class CreateCredentialActivity : ComponentActivity() {
126127
url = uiState.authServer.url,
127128
redirectUrl = uiState.authServer.redirectUrl,
128129
onDone = { code ->
129-
viewModel.onCode(code)
130+
viewModel.onCode(code, uiState.authServer.redirectUrl)
130131
}
131132
)
132133
}
@@ -348,34 +349,39 @@ class CreateCredentialActivity : ComponentActivity() {
348349
redirectUrl: String,
349350
onDone: (String) -> Unit
350351
) {
351-
AndroidView(factory = {
352-
WebView(it).apply {
353-
settings.javaScriptEnabled = true
354-
this.layoutParams = ViewGroup.LayoutParams(
355-
ViewGroup.LayoutParams.MATCH_PARENT,
356-
ViewGroup.LayoutParams.MATCH_PARENT
357-
)
358-
this.webViewClient = object : WebViewClient() {
359-
override fun shouldOverrideUrlLoading(
360-
view: WebView?,
361-
request: WebResourceRequest?
362-
): Boolean {
352+
LazyColumn {
353+
item {
354+
AndroidView(factory = {
355+
WebView(it).apply {
356+
clearCache(true)
357+
settings.javaScriptEnabled = true
358+
this.layoutParams = ViewGroup.LayoutParams(
359+
ViewGroup.LayoutParams.MATCH_PARENT,
360+
ViewGroup.LayoutParams.MATCH_PARENT
361+
)
362+
this.webViewClient = object : WebViewClient() {
363+
override fun shouldOverrideUrlLoading(
364+
view: WebView?,
365+
request: WebResourceRequest?
366+
): Boolean {
363367

364-
request?.let {
368+
request?.let {
365369

366-
if (request.url.toString().startsWith("$redirectUrl/")) {
367-
request.url.getQueryParameter("code")?.let { code ->
368-
onDone(code)
370+
if (request.url.toString().startsWith("$redirectUrl/")) {
371+
request.url.getQueryParameter("code")?.let { code ->
372+
onDone(code)
373+
}
374+
}
369375
}
376+
return super.shouldOverrideUrlLoading(view, request)
370377
}
371378
}
372-
return super.shouldOverrideUrlLoading(view, request)
373379
}
374-
}
380+
}, update = {
381+
it.loadUrl(url)
382+
})
375383
}
376-
}, update = {
377-
it.loadUrl(url)
378-
})
384+
}
379385
}
380386
}
381387

app/src/main/java/com/credman/cmwallet/createcred/CreateCredentialViewModel.kt

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ import androidx.lifecycle.ViewModel
1616
import androidx.lifecycle.viewModelScope
1717
import com.credman.cmwallet.CmWalletApplication
1818
import com.credman.cmwallet.CmWalletApplication.Companion.TAG
19+
import com.credman.cmwallet.CmWalletApplication.Companion.TEST_VCI_CLIENT_ID
1920
import com.credman.cmwallet.CmWalletApplication.Companion.computeClientId
2021
import com.credman.cmwallet.data.model.Credential
2122
import com.credman.cmwallet.data.model.CredentialDisplayData
2223
import com.credman.cmwallet.data.model.CredentialItem
2324
import com.credman.cmwallet.data.model.CredentialKeySoftware
2425
import com.credman.cmwallet.data.room.CredentialDatabaseItem
25-
import com.credman.cmwallet.getcred.GetCredentialActivity
2626
import com.credman.cmwallet.getcred.createOpenID4VPResponse
2727
import com.credman.cmwallet.loadECPrivateKey
2828
import com.credman.cmwallet.openid4vci.OpenId4VCI
@@ -89,7 +89,7 @@ class CreateCredentialViewModel : ViewModel() {
8989
Log.d(TAG, "Done")
9090
}
9191

92-
fun onCode(code: String) {
92+
fun onCode(code: String, redirectUrl: String?) {
9393
uiState = uiState.copy(authServer = null)
9494
viewModelScope.launch {
9595
// Figure out auth server
@@ -102,7 +102,10 @@ class CreateCredentialViewModel : ViewModel() {
102102
val tokenResponse = openId4VCI.requestTokenFromEndpoint(
103103
authServer, TokenRequest(
104104
grantType = "authorization_code",
105-
code = code
105+
code = code,
106+
redirectUri = redirectUrl,
107+
scope = openId4VCI.credentialOffer.credentialConfigurationIds.first(),
108+
codeVerifier = openId4VCI.codeVerifier
106109
)
107110
)
108111
Log.i(TAG, "tokenResponse $tokenResponse")
@@ -112,17 +115,49 @@ class CreateCredentialViewModel : ViewModel() {
112115

113116
@OptIn(ExperimentalUuidApi::class)
114117
private suspend fun processToken(tokenResponse: TokenResponse) {
118+
val newCredentials = mutableListOf<CredentialItem>()
119+
tokenResponse.scopes?.split(" ")?.forEach { scope ->
120+
val credentialResponse = openId4VCI.requestCredentialFromEndpoint(
121+
accessToken = tokenResponse.accessToken,
122+
credentialRequest = CredentialRequest(
123+
credentialConfigurationId = scope,
124+
proof = openId4VCI.createProofJwt(publicKey, privateKey)
125+
)
126+
)
127+
Log.i(TAG, "credentialResponse $credentialResponse")
128+
val config = openId4VCI.credentialOffer.issuerMetadata.credentialConfigurationsSupported[scope]!!
129+
val display = credentialResponse.display?.firstOrNull()
130+
val configDisplay = config.credentialMetadata?.display?.firstOrNull()
131+
val newCredentialItem = CredentialItem(
132+
id = Uuid.random().toHexString(),
133+
config = config,
134+
displayData = CredentialDisplayData(
135+
title = display?.name ?: configDisplay?.name ?: "Unknown",
136+
subtitle = display?.description ?: configDisplay?.description,
137+
icon = display?.logo?.uri.imageUriToImageB64()
138+
),
139+
credentials = credentialResponse.credentials!!.map {
140+
Credential(
141+
key = CredentialKeySoftware(
142+
publicKey = tmpPublicKey,
143+
privateKey = tmpKey
144+
),
145+
credential = it.credential
146+
)
147+
}
148+
)
149+
newCredentials.add(newCredentialItem)
150+
}
115151
tokenResponse.authorizationDetails?.forEach { authDetail ->
116152
when (authDetail) {
117153
is AuthorizationDetailResponseOpenIdCredential -> {
118-
val newCredentials = mutableListOf<CredentialItem>()
119154
authDetail.credentialIdentifiers.forEach { credentialId ->
120155
val credentialResponse = openId4VCI.requestCredentialFromEndpoint(
121156
accessToken = tokenResponse.accessToken,
122157
credentialRequest = CredentialRequest(
123158
credentialIdentifier = credentialId,
124159
proof = openId4VCI.createProofJwt(publicKey, privateKey)
125-
)
160+
),
126161
)
127162
Log.i(TAG, "credentialResponse $credentialResponse")
128163
val config = openId4VCI.credentialOffer.issuerMetadata.credentialConfigurationsSupported[authDetail.credentialConfigurationId]!!
@@ -147,10 +182,10 @@ class CreateCredentialViewModel : ViewModel() {
147182
)
148183
newCredentials.add(newCredentialItem)
149184
}
150-
uiState = uiState.copy(credentialsToSave = newCredentials, authServer = null)
151185
}
152186
}
153187
}
188+
uiState = uiState.copy(credentialsToSave = newCredentials, authServer = null)
154189
}
155190

156191
@OptIn(ExperimentalUuidApi::class)
@@ -226,7 +261,10 @@ class CreateCredentialViewModel : ViewModel() {
226261
val tokenResponse = openId4VCI.requestTokenFromEndpoint(
227262
authServer, TokenRequest(
228263
grantType = "urn:ietf:params:oauth:grant-type:pre-authorized_code",
229-
preAuthorizedCode = grant?.preAuthorizedCode
264+
preAuthorizedCode = grant?.preAuthorizedCode,
265+
txCode = "123456",
266+
clientId = TEST_VCI_CLIENT_ID,
267+
scope = openId4VCI.credentialOffer.credentialConfigurationIds.first()
230268
)
231269
)
232270
Log.i(TAG, "tokenResponse $tokenResponse")
@@ -253,13 +291,25 @@ class CreateCredentialViewModel : ViewModel() {
253291
uiState = uiState.copy(vpResponse = selectedCredential, tmpCode = grant)
254292

255293
} else {
256-
val authServerUrl = Uri.parse(openId4VCI.authEndpoint(authServer))
257-
.buildUpon()
258-
.appendQueryParameter("response_type", "code")
259-
.appendQueryParameter("state", Uuid.random().toString())
260-
.appendQueryParameter("redirect_uri", "http://localhost")
261-
.appendQueryParameter("issuer_state", grant?.issuerState ?: "")
262-
.build()
294+
val parResponse = openId4VCI.requestParEndpoint(TEST_VCI_CLIENT_ID)
295+
val authServerUrl = if (parResponse == null) {
296+
Uri.parse(openId4VCI.authEndpoint(authServer))
297+
.buildUpon()
298+
.appendQueryParameter("response_type", "code")
299+
.appendQueryParameter("state", Uuid.random().toString())
300+
.appendQueryParameter("redirect_uri", "http://localhost")
301+
.appendQueryParameter("issuer_state", grant?.issuerState ?: "")
302+
.appendQueryParameter("scope",
303+
openId4VCI.credentialOffer.credentialConfigurationIds.first()
304+
)
305+
.build()
306+
} else {
307+
Uri.parse(openId4VCI.authEndpoint(authServer))
308+
.buildUpon()
309+
.appendQueryParameter("client_id", TEST_VCI_CLIENT_ID)
310+
.appendQueryParameter("request_uri", parResponse.requestUri)
311+
.build()
312+
}
263313

264314
Log.d(TAG, "authServerUrl: $authServerUrl")
265315
uiState = uiState.copy(authServer = AuthServerUiState(

app/src/main/java/com/credman/cmwallet/data/repository/CredentialRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ class CredentialRepository {
184184
JSONObject().apply {
185185
val displayName = displayConfig?.claims?.firstOrNull{
186186
JSONArray(it.path) == currPath
187-
}?.display?.first()?.name
187+
}?.display?.first()?.name ?: key
188188
putOpt(DISPLAY, displayName)
189189
putOpt(VALUE, v)
190190
}

0 commit comments

Comments
 (0)