@@ -91,84 +91,91 @@ class CreateCredentialViewModel : ViewModel() {
9191 viewModelScope.launch {
9292 // Figure out auth server
9393 val authServer = openId4VCI.authServerIdentifier()
94+ val scope = openId4VCI.credentialOffer.credentialConfigurationIds.first()
9495 val tokenResponse = openId4VCI.requestTokenFromEndpoint(
9596 authServer, TokenRequest (
9697 grantType = " authorization_code" ,
9798 code = code,
9899 redirectUri = redirectUrl,
99100 clientId = WALLET_CLIENT_ID ,
100- scope = openId4VCI.credentialOffer.credentialConfigurationIds.first() ,
101+ scope = scope ,
101102 codeVerifier = openId4VCI.codeVerifier
102103 )
103104 )
104105 Log .i(TAG , " tokenResponse $tokenResponse " )
105- processToken(tokenResponse)
106+ processToken(tokenResponse, tokenRequestScope = scope )
106107 }
107108 }
108109
109110 @OptIn(ExperimentalUuidApi ::class )
110- private suspend fun processToken (tokenResponse : TokenResponse ) {
111+ private suspend fun processToken (
112+ tokenResponse : TokenResponse ,
113+ tokenRequestScope : String? // Fallback when the tokenResponse doesn't contain a scope property
114+ ) {
111115 val newCredentials = mutableListOf<CredentialItem >()
112- tokenResponse.scopes?.split(" " )?.forEach { scope ->
113- val deviceKeys: MutableList <KeyPair > = mutableListOf ()
114- val kpg = KeyPairGenerator .getInstance(" EC" )
115- kpg.initialize(ECGenParameterSpec (" secp256r1" ))
116- for (i in 0 .. < (openId4VCI.credentialOffer.issuerMetadata.batchCredentialIssuance?.batchSize ? : 1 )) {
117- deviceKeys.add(kpg.genKeyPair())
118- }
119- val credentialResponse = openId4VCI.requestCredentialFromEndpoint(
120- accessToken = tokenResponse.accessToken,
121- credentialRequest = CredentialRequest (
122- credentialConfigurationId = scope,
123- proofs = openId4VCI.createProofJwt(deviceKeys)
116+ if (tokenResponse.authorizationDetails == null ) {
117+ val scopes = (tokenResponse.scopes ? : tokenRequestScope)?.split(" " )
118+ scopes?.forEach { scope ->
119+ val deviceKeys: MutableList <KeyPair > = mutableListOf ()
120+ val kpg = KeyPairGenerator .getInstance(" EC" )
121+ kpg.initialize(ECGenParameterSpec (" secp256r1" ))
122+ for (i in 0 .. < (openId4VCI.credentialOffer.issuerMetadata.batchCredentialIssuance?.batchSize ? : 1 )) {
123+ deviceKeys.add(kpg.genKeyPair())
124+ }
125+ val credentialResponse = openId4VCI.requestCredentialFromEndpoint(
126+ accessToken = tokenResponse.accessToken,
127+ credentialRequest = CredentialRequest (
128+ credentialConfigurationId = scope,
129+ proofs = openId4VCI.createProofJwt(deviceKeys)
130+ )
124131 )
125- )
126- Log .i( TAG , " credentialResponse $credentialResponse " )
127- val config = openId4VCI.credentialOffer.issuerMetadata.credentialConfigurationsSupported[scope] !!
128- val display = credentialResponse .display?.firstOrNull()
129- val configDisplay = config.credentialMetadata?.display?.firstOrNull()
130- val newCredentialItem = CredentialItem (
131- id = Uuid .random().toHexString() ,
132- config = config,
133- displayData = CredentialDisplayData (
134- title = display?.name ? : configDisplay?.name ? : " Unknown " ,
135- subtitle = display?.description ? : configDisplay?.description,
136- icon = display?.logo?.uri.imageUriToImageB64()
137- ),
138- credentials = credentialResponse.credentials !! .map {
139- val deviceKeyPair = when (config) {
140- is CredentialConfigurationMDoc -> {
141- val mdoc = MDoc (it.credential.decodeBase64UrlNoPadding())
142- val deviceKey = mdoc.deviceKey
143- deviceKeys.firstOrNull {
144- val public = it. public as ECPublicKey
145- val x = String (public.w.affineX .toFixedByteArray(32 ))
146- val y = String (public.w.affineY.toFixedByteArray( 32 ))
147- x == deviceKey.first && y == deviceKey.second
132+ Log .i( TAG , " credentialResponse $credentialResponse " )
133+ val config = openId4VCI.credentialOffer.issuerMetadata.credentialConfigurationsSupported[scope] !!
134+ val display = credentialResponse.display?.firstOrNull()
135+ val configDisplay = config.credentialMetadata? .display?.firstOrNull()
136+ val newCredentialItem = CredentialItem (
137+ id = Uuid .random().toHexString(),
138+ config = config ,
139+ displayData = CredentialDisplayData (
140+ title = display?.name ? : configDisplay?.name ? : " Unknown " ,
141+ subtitle = display?.description ? : configDisplay?.description ,
142+ icon = display?.logo?.uri.imageUriToImageB64()
143+ ),
144+ credentials = credentialResponse.credentials !! .map {
145+ val deviceKeyPair = when (config) {
146+ is CredentialConfigurationMDoc -> {
147+ val mdoc = MDoc (it.credential.decodeBase64UrlNoPadding())
148+ val deviceKey = mdoc.deviceKey
149+ deviceKeys.firstOrNull {
150+ val public = it. public as ECPublicKey
151+ val x = String ( public.w.affineX.toFixedByteArray( 32 ))
152+ val y = String (public.w.affineY .toFixedByteArray(32 ))
153+ x == deviceKey.first && y == deviceKey.second
154+ }
148155 }
149- }
150- is CredentialConfigurationSdJwtVc -> {
151- val issuerJwtString = it.credential.split( ' ~ ' )[ 0 ]
152- val cnfKey = IssuerJwt (issuerJwtString).payload.getJSONObject( " cnf " ).getJSONObject( " jwk " )
153- deviceKeys.firstOrNull {
154- val public = it. public as ECPublicKey
155- val x = public.w.affineX .toFixedByteArray(32 ).toBase64UrlNoPadding()
156- val y = public.w.affineY.toFixedByteArray( 32 ).toBase64UrlNoPadding( )
157- x == cnfKey.getString( " x " ) && y == cnfKey.getString( " y " )
156+ is CredentialConfigurationSdJwtVc -> {
157+ val issuerJwtString = it.credential.split( ' ~ ' )[ 0 ]
158+ val cnfKey = IssuerJwt (issuerJwtString).payload.getJSONObject( " cnf " ).getJSONObject( " jwk " )
159+ deviceKeys.firstOrNull {
160+ val public = it. public as ECPublicKey
161+ val x = public.w.affineX.toFixedByteArray( 32 ).toBase64UrlNoPadding()
162+ val y = public.w.affineY .toFixedByteArray(32 ).toBase64UrlNoPadding()
163+ x == cnfKey.getString( " x " ) && y == cnfKey.getString( " y " )
164+ }
158165 }
166+ else -> throw UnsupportedOperationException (" Unknown configuration $config " )
159167 }
160- else -> throw UnsupportedOperationException (" Unknown configuration $config " )
168+ Credential (
169+ key = CredentialKeySoftware (
170+ publicKey = Base64 .encodeToString(deviceKeyPair!! .public.encoded, Base64 .URL_SAFE or Base64 .NO_PADDING or Base64 .NO_WRAP ),
171+ privateKey = Base64 .encodeToString(deviceKeyPair.private.encoded, Base64 .URL_SAFE or Base64 .NO_PADDING or Base64 .NO_WRAP ),
172+ ),
173+ credential = it.credential
174+ )
161175 }
162- Credential (
163- key = CredentialKeySoftware (
164- publicKey = Base64 .encodeToString(deviceKeyPair!! .public.encoded, Base64 .URL_SAFE or Base64 .NO_PADDING or Base64 .NO_WRAP ),
165- privateKey = Base64 .encodeToString(deviceKeyPair.private.encoded, Base64 .URL_SAFE or Base64 .NO_PADDING or Base64 .NO_WRAP ),
166- ),
167- credential = it.credential
168- )
169- }
170- )
171- newCredentials.add(newCredentialItem)
176+ )
177+ newCredentials.add(newCredentialItem)
178+ }
172179 }
173180 tokenResponse.authorizationDetails?.forEach { authDetail ->
174181 when (authDetail) {
@@ -300,18 +307,18 @@ class CreateCredentialViewModel : ViewModel() {
300307
301308 if (openId4VCI.credentialOffer.grants!! .preAuthorizedCode != null ) {
302309 val grant = openId4VCI.credentialOffer.grants!! .preAuthorizedCode
303-
310+ val scope = openId4VCI.credentialOffer.credentialConfigurationIds.first()
304311 val tokenResponse = openId4VCI.requestTokenFromEndpoint(
305312 authServer, TokenRequest (
306313 grantType = " urn:ietf:params:oauth:grant-type:pre-authorized_code" ,
307314 preAuthorizedCode = grant?.preAuthorizedCode,
308315 txCode = " 123456" ,
309316 clientId = WALLET_CLIENT_ID ,
310- scope = openId4VCI.credentialOffer.credentialConfigurationIds.first()
317+ scope = scope
311318 )
312319 )
313320 Log .i(TAG , " tokenResponse $tokenResponse " )
314- processToken(tokenResponse)
321+ processToken(tokenResponse, tokenRequestScope = scope )
315322
316323 } else if (openId4VCI.credentialOffer.grants!! .authorizationCode != null ) {
317324 val grant = openId4VCI.credentialOffer.grants!! .authorizationCode!!
0 commit comments