Skip to content

Commit

Permalink
Fix #3: SDJWT Issuance and Verification
Browse files Browse the repository at this point in the history
  • Loading branch information
josmilan committed Mar 26, 2024
1 parent 16ed4f7 commit 8fed1d6
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 70 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.1")

implementation("com.github.decentralised-dataexchange:presentation-exchange-sdk-android:2024.3.1")
}
17 changes: 15 additions & 2 deletions app/src/main/java/com/ewc/eudiwalletoidcandroid/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.ewc.eudi_wallet_oidc_android.services.did.DIDService
import com.ewc.eudiwalletoidcandroid.databinding.ActivityMainBinding
import com.github.decentraliseddataexchange.presentationexchangesdk.PresentationExchange
import com.github.decentraliseddataexchange.presentationexchangesdk.models.MatchedCredential
import com.google.gson.Gson
import io.igrant.qrcode_scanner_android.qrcode.utils.QRScanner

class MainActivity : AppCompatActivity() {
Expand All @@ -29,13 +32,20 @@ class MainActivity : AppCompatActivity() {
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel?.subJwk = DIDService().createJWK()
viewModel?.did = DIDService().createDID(viewModel?.subJwk!!)

initClicks()
}

private fun initClicks() {
binding.btnCreateDID.setOnClickListener {
viewModel?.subJwk = DIDService().createJWK()
viewModel?.did = DIDService().createDID(viewModel?.subJwk!!)

viewModel?.displayText?.value = "Sub JWK : \n ${Gson().toJson(viewModel?.subJwk)}\n\n"
viewModel?.displayText?.value =
"${viewModel?.displayText?.value}Did : ${viewModel?.did}\n\n"
}

binding.addCredential.setOnClickListener {
binding.tvCredential.text = ""
QRScanner().withLocale("en").start(
Expand Down Expand Up @@ -75,6 +85,9 @@ class MainActivity : AppCompatActivity() {
""
}

viewModel?.displayText?.value =
"${viewModel?.displayText?.value}Scanned data : $url\n\n"

viewModel?.issueCredential(url ?: "")
}

Expand Down
141 changes: 120 additions & 21 deletions app/src/main/java/com/ewc/eudiwalletoidcandroid/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import androidx.lifecycle.ViewModel
import com.ewc.eudi_wallet_oidc_android.models.AuthorisationServerWellKnownConfiguration
import com.ewc.eudi_wallet_oidc_android.models.CredentialOffer
import com.ewc.eudi_wallet_oidc_android.models.IssuerWellKnownConfiguration
import com.ewc.eudi_wallet_oidc_android.models.PresentationRequest
import com.ewc.eudi_wallet_oidc_android.models.TokenResponse
import com.ewc.eudi_wallet_oidc_android.models.WrappedTokenResponse
import com.ewc.eudi_wallet_oidc_android.services.codeVerifier.CodeVerifierService
import com.ewc.eudi_wallet_oidc_android.services.did.DIDService
import com.ewc.eudi_wallet_oidc_android.services.discovery.DiscoveryService
import com.ewc.eudi_wallet_oidc_android.services.issue.IssueService
import com.ewc.eudi_wallet_oidc_android.services.sdjwt.SDJWTService
import com.ewc.eudi_wallet_oidc_android.services.verification.VerificationService
import com.google.gson.Gson
import com.nimbusds.jose.jwk.ECKey
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -26,10 +30,12 @@ class MainViewModel : ViewModel() {

var isPreAuthorised = MutableLiveData<Boolean>(false)

var displayText = MutableLiveData<String?>("")

var credentialJwt = MutableLiveData<String?>("")

private lateinit var codeVerifier: String
private var tokenResponse: TokenResponse? = null
private var tokenResponse: WrappedTokenResponse? = null
private var authConfig: AuthorisationServerWellKnownConfiguration? = null
private var issuerConfig: IssuerWellKnownConfiguration? = null
private var offerCredential: CredentialOffer? = null
Expand All @@ -48,15 +54,25 @@ class MainViewModel : ViewModel() {

// Discovery
issuerConfig =
DiscoveryService().getIssuerConfig(offerCredential?.credentialIssuer)
DiscoveryService().getIssuerConfig("${offerCredential?.credentialIssuer}/.well-known/openid-credential-issuer")

authConfig =
DiscoveryService().getAuthConfig(
issuerConfig?.authorizationServer ?: issuerConfig?.issuer
"${issuerConfig?.authorizationServer ?: issuerConfig?.issuer}/.well-known/openid-configuration"
)

// Generating code verifier
codeVerifier = CodeVerifierService().generateCodeVerifier()

withContext(Dispatchers.Main) {
displayText.value =
"${displayText?.value}Issuer Config : \n${Gson().toJson(issuerConfig)}\n\n"
displayText.value =
"${displayText.value}Auth Config : \n${Gson().toJson(authConfig)}\n\n"
displayText.value =
"${displayText.value}Code verifier : \n$codeVerifier\n\n"
}

if (offerCredential?.grants?.preAuthorizationCode?.preAuthorizedCode != null) {
// pre authorized code flow
if (offerCredential?.grants?.preAuthorizationCode?.userPinRequired == true) {
Expand Down Expand Up @@ -107,20 +123,26 @@ class MainViewModel : ViewModel() {
did,
subJwk,
issuerConfig?.credentialIssuer,
tokenResponse?.cNonce,
tokenResponse?.tokenResponse?.cNonce,
offerCredential,
issuerConfig?.credentialEndpoint,
tokenResponse?.accessToken
tokenResponse?.tokenResponse?.accessToken
)

withContext(Dispatchers.Main) {
if (credential?.credential != null) {
credentialJwt.value = credential.credential
if (credential?.credentialResponse != null) {
displayText.value =
"${displayText.value}Token : \n${Gson().toJson(tokenResponse)}\n\n"
displayText.value =
"${displayText.value}Credential : \n${Gson().toJson(credential)}\n\n"


credentialJwt.value = credential.credentialResponse?.credential
isLoading.value = false
}
}

fetchDeferredCredential(credential?.acceptanceToken)
fetchDeferredCredential(credential?.credentialResponse?.acceptanceToken)
}

// fetching deferred credential
Expand All @@ -136,11 +158,21 @@ class MainViewModel : ViewModel() {
issuerConfig?.deferredCredentialEndpoint
)

if (credential?.credential != null) {
if (credential?.credentialResponse?.credential != null) {

withContext(Dispatchers.Main) {
if (credential.credential != null) {
credentialJwt.value = credential.credential
if (credential.credentialResponse?.credential != null) {

displayText.value =
"${displayText.value}Token : \n${Gson().toJson(tokenResponse)}\n\n"
displayText.value =
"${displayText.value}Credential : \n${
Gson().toJson(
credential
)
}\n\n"

credentialJwt.value = credential.credentialResponse?.credential
isLoading.value = false
}
}
Expand All @@ -167,27 +199,94 @@ class MainViewModel : ViewModel() {
}
}

fun verifyCredential(url:String){
fun verifyCredential(url: String) {
CoroutineScope(Dispatchers.Main).launch {
val presentationRequest =
VerificationService().processAuthorisationRequest(url)

val subJwk = DIDService().createJWK()
val did = DIDService().createDID(subJwk)

val issuerConfig =
DiscoveryService().getIssuerConfig(presentationRequest?.clientId)
val authConfig =
DiscoveryService().getAuthConfig(issuerConfig?.authorizationServer)
withContext(Dispatchers.Main) {
displayText.value =
"${displayText.value}Verification started\n\n"
displayText.value =
"${displayText.value}Presentation Request : \n${
Gson().toJson(
presentationRequest
)
}\n\n"

displayText.value =
"${displayText.value}Did : \n${did}\n\n"

displayText.value =
"${displayText.value}private key : \n${Gson().toJson(subJwk)}\n\n"
}


if (presentationRequest != null) {
val code = VerificationService().sendVPToken(
did = did,
subJwk = subJwk,
presentationRequest = presentationRequest,
credentialList = listOf(credentialJwt.value?:"")

val presentationDefinition =
VerificationService().processPresentationDefinition(presentationRequest.presentationDefinition)

val allCredentials = listOf(credentialJwt.value)

val filteredCredentials = takeFirstElementInEachList(
VerificationService().filterCredentials(
allCredentials,
presentationDefinition
),
presentationDefinition.format?.containsKey("sd_jwt") == true,
presentationRequest,
subJwk
)

if (filteredCredentials.isNotEmpty()) {
val code = VerificationService().sendVPToken(
did = did,
subJwk = subJwk,
presentationRequest = presentationRequest,
credentialList = filteredCredentials
)

withContext(Dispatchers.Main) {
displayText.value =
"${displayText.value} ${if (code != null) "Verification success" else "Verification failed"}\n\n"
}
} else {
withContext(Dispatchers.Main) {
displayText.value =
"${displayText.value}No valid credentials\n\n"
}
}
}
}
}

//since we are doing selection of cards here, we pick the latest card
private fun takeFirstElementInEachList(
filterCredentials: List<List<String>>,
isSdJwt: Boolean,
presentationRequest: PresentationRequest,
subJwk: ECKey
): List<String> {
val response: MutableList<String> = mutableListOf()
filterCredentials.forEach {
if (it.isNotEmpty())
if (isSdJwt)
response.add(
SDJWTService().createSDJWTR(
it.first(),
presentationRequest,
subJwk
) ?: ""
)
else {
response.add(it.first())
}

}
return response
}
}
Loading

0 comments on commit 8fed1d6

Please sign in to comment.