-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Parse a vci request into structured object
- Loading branch information
Showing
3 changed files
with
231 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
app/src/main/java/com/credman/cmwallet/openid4vci/OpenId4VCI.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.credman.cmwallet.openid4vci | ||
|
||
import org.json.JSONObject | ||
|
||
class OpenId4VCI(val request: String) { | ||
val requestJson: JSONObject = JSONObject(request) | ||
|
||
val credentialIssuer: String | ||
val credentialConfigurationIds: List<String> | ||
val credentialConfigurationsSupportedMap: Map<String, CredConfigsSupportedItem> | ||
|
||
init { | ||
require(requestJson.has(CREDENTIAL_ISSUER)) { "Issuance request must contain $CREDENTIAL_ISSUER" } | ||
require(requestJson.has(CREDENTIAL_CONFIGURATION_IDS)) { "Issuance request must contain $CREDENTIAL_CONFIGURATION_IDS" } | ||
// This should be required for the DC API browser profile | ||
require(requestJson.has(ISSUER_METADATA)) { "Issuance request must contain $ISSUER_METADATA" } | ||
|
||
credentialIssuer = requestJson.getString(CREDENTIAL_ISSUER) | ||
credentialConfigurationIds = requestJson.getJSONArray(CREDENTIAL_CONFIGURATION_IDS).let { | ||
val ids = mutableListOf<String>() | ||
for (i in 0..<it.length()) { | ||
ids.add(it.getString(i)) | ||
} | ||
ids | ||
} | ||
|
||
val issuerMetadataJson = requestJson.getJSONObject(ISSUER_METADATA) | ||
require(issuerMetadataJson.has(CREDENTIAL_CONFIGURATION_SUPPORTED)) { "Issuance request must contain $CREDENTIAL_CONFIGURATION_SUPPORTED" } | ||
val credConfigSupportedJson = issuerMetadataJson.getJSONObject(CREDENTIAL_CONFIGURATION_SUPPORTED) | ||
val itr = credConfigSupportedJson.keys() | ||
val tmpMap = mutableMapOf<String, CredConfigsSupportedItem>() | ||
while (itr.hasNext()) { | ||
val configId = itr.next() | ||
val item = credConfigSupportedJson.getJSONObject(configId) | ||
tmpMap[configId] = CredConfigsSupportedItem.createFrom(credConfigSupportedJson.getJSONObject(configId)) | ||
} | ||
credentialConfigurationsSupportedMap = tmpMap | ||
} | ||
} |
190 changes: 190 additions & 0 deletions
190
app/src/main/java/com/credman/cmwallet/openid4vci/RequestDataModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
package com.credman.cmwallet.openid4vci | ||
|
||
import org.json.JSONObject | ||
|
||
data class CredConfigsSupportedDisplay( | ||
val name: String, | ||
val locale: String? = null, | ||
val logo: CredConfigsSupportedDisplayLogo? = null, | ||
val description: String? = null, | ||
val backgroundColor: String? = null, | ||
// `https:` or `data:` scheme | ||
val backgroundImage: String? = null, | ||
val textColor: String, | ||
) { | ||
constructor(json: JSONObject) : this( | ||
name = json.getString(NAME), | ||
locale = json.optString(LOCALE), | ||
logo = json.optJSONObject(LOGO)?.let { | ||
CredConfigsSupportedDisplayLogo(uri = it.getString(URI), altText = it.optString(ALT_TEXT)) | ||
}, | ||
description = json.optString(DESCRIPTION), | ||
backgroundColor = json.optString(BACKGROUND_COLOR), | ||
backgroundImage = json.optJSONObject(BACKGROUND_IMAGE)?.getString(URI), | ||
textColor = json.optString(TEXT_COLOR), | ||
) | ||
} | ||
|
||
private fun JSONObject.getDisplays(): List<CredConfigsSupportedDisplay>? = | ||
this.optJSONArray(DISPLAY)?.let { | ||
val out = mutableListOf<CredConfigsSupportedDisplay>() | ||
for (i in 0..< it.length()) { | ||
out.add(CredConfigsSupportedDisplay(it.getJSONObject(i))) | ||
} | ||
out | ||
} | ||
|
||
data class CredConfigsSupportedDisplayLogo( | ||
val uri: String, | ||
val altText: String? | ||
) | ||
|
||
sealed class CredConfigsSupportedItem( | ||
val format: String, | ||
val cryptographicBindingMethodsSupported: List<String>? = null, | ||
val credentialSigningAlgValuesSupported: List<String>? = null, | ||
val display: List<CredConfigsSupportedDisplay>? = null, | ||
) { | ||
companion object { | ||
fun createFrom(json: JSONObject): CredConfigsSupportedItem { | ||
return if (json.has(DOCTYPE)) { | ||
MdocCredConfigsSupportedItem(json) | ||
} else { | ||
UnknownCredConfigsSupportedItem(json) | ||
} | ||
} | ||
} | ||
} | ||
|
||
class UnknownCredConfigsSupportedItem( | ||
format: String, | ||
cryptographicBindingMethodsSupported: List<String>? = null, | ||
credentialSigningAlgValuesSupported: List<String>? = null, | ||
display: List<CredConfigsSupportedDisplay>? = null, | ||
) : CredConfigsSupportedItem( | ||
format, | ||
cryptographicBindingMethodsSupported, | ||
credentialSigningAlgValuesSupported, | ||
display | ||
) { | ||
constructor(json: JSONObject): this( | ||
format = json.getString(FORMAT), | ||
cryptographicBindingMethodsSupported = json.getCryptographicBindingMethodsSupported(), | ||
credentialSigningAlgValuesSupported = json.getCredentialSigningAlgValuesSupported(), | ||
display = json.getDisplays(), | ||
) | ||
} | ||
|
||
class MdocCredConfigsSupportedItem( | ||
format: String, | ||
cryptographicBindingMethodsSupported: List<String>? = null, | ||
credentialSigningAlgValuesSupported: List<String>? = null, | ||
display: List<CredConfigsSupportedDisplay>? = null, | ||
val doctype: String, | ||
// Namespace as key e.g. "org.iso.18013.5.1" | ||
val claims: Map<String, NamespacedClaims>?, | ||
) : CredConfigsSupportedItem( | ||
format, | ||
cryptographicBindingMethodsSupported, | ||
credentialSigningAlgValuesSupported, | ||
display | ||
) { | ||
constructor(json: JSONObject): this( | ||
format = json.getString(FORMAT), | ||
cryptographicBindingMethodsSupported = json.getCryptographicBindingMethodsSupported(), | ||
credentialSigningAlgValuesSupported = json.getCredentialSigningAlgValuesSupported(), | ||
display = json.getDisplays(), | ||
doctype = json.getString(DOCTYPE), | ||
claims = json.optJSONObject(CLAIMS)?.let { | ||
val keys = it.keys() | ||
val out = mutableMapOf<String, NamespacedClaims>() | ||
while(keys.hasNext()) { | ||
val key = keys.next() | ||
out[key] = NamespacedClaims(it.getJSONObject(key)) | ||
} | ||
out | ||
} | ||
) | ||
} | ||
|
||
data class NamespacedClaims( | ||
val values: Map<String, Claim> | ||
) { | ||
constructor(json: JSONObject) : this( | ||
values = json.let { | ||
val keys = it.keys() | ||
val out = mutableMapOf<String, Claim>() | ||
while(keys.hasNext()) { | ||
val key = keys.next() | ||
out[key] = Claim(it.getJSONObject(key)) | ||
} | ||
out | ||
} | ||
) | ||
} | ||
|
||
data class Claim( | ||
val display: List<ClaimDisplay>? = null, | ||
val mandatory: Boolean = false, | ||
val valueType: String? = null, | ||
) { | ||
constructor(json: JSONObject) : this( | ||
display = json.optJSONArray(DISPLAY)?.let { | ||
val out = mutableListOf<ClaimDisplay>() | ||
for (i in 0..< it.length()) { | ||
out.add(ClaimDisplay(it.getJSONObject(i))) | ||
} | ||
out | ||
}, | ||
mandatory = json.optBoolean(MANDATORY, false), | ||
valueType = json.optString(VALUE_TYPE), | ||
) | ||
} | ||
|
||
data class ClaimDisplay( | ||
val name: String?, | ||
val locale: String?, | ||
) { | ||
constructor(json: JSONObject) : this(json.optString(NAME), json.optString(LOCALE)) | ||
} | ||
|
||
private fun JSONObject.getCryptographicBindingMethodsSupported(): List<String>? = | ||
this.optJSONArray(CRYPTOGRAPHIC_BINDING_METHODS_SUPPORTED)?.let { | ||
val out = mutableListOf<String>() | ||
for (i in 0..< it.length()) { | ||
out.add(it.getString(i)) | ||
} | ||
out | ||
} | ||
|
||
private fun JSONObject.getCredentialSigningAlgValuesSupported(): List<String>? = | ||
this.optJSONArray(CREDENTIAL_SIGNING_ALG_VALUES_SUPPORTED)?.let { | ||
val out = mutableListOf<String>() | ||
for (i in 0..< it.length()) { | ||
out.add(it.getString(i)) | ||
} | ||
out | ||
} | ||
|
||
|
||
internal const val CREDENTIAL_ISSUER = "credential_issuer" | ||
internal const val CREDENTIAL_CONFIGURATION_IDS = "credential_configuration_ids" | ||
internal const val ISSUER_METADATA = "issuer_metadata" | ||
internal const val CREDENTIAL_CONFIGURATION_SUPPORTED = "credential_configurations_supported" | ||
internal const val CRYPTOGRAPHIC_BINDING_METHODS_SUPPORTED = "cryptographic_binding_methods_supported" | ||
internal const val CREDENTIAL_SIGNING_ALG_VALUES_SUPPORTED = "credential_signing_alg_values_supported" | ||
internal const val FORMAT = "format" | ||
internal const val DOCTYPE = "doctype" | ||
internal const val DISPLAY = "display" | ||
internal const val NAME = "name" | ||
internal const val URI = "uri" | ||
internal const val LOCALE = "locale" | ||
internal const val MANDATORY = "mandatory" | ||
internal const val LOGO = "logo" | ||
internal const val DESCRIPTION = "description" | ||
internal const val CLAIMS = "claims" | ||
internal const val BACKGROUND_IMAGE = "background_image" | ||
internal const val BACKGROUND_COLOR = "background_color" | ||
internal const val TEXT_COLOR = "text_color" | ||
internal const val VALUE_TYPE = "value_type" | ||
internal const val ALT_TEXT = "alt_text" |