Skip to content

Commit

Permalink
wip: oid4vp integration
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Tate <[email protected]>
  • Loading branch information
Ryanmtate committed Oct 9, 2024
1 parent 4a78334 commit 1e47f6a
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 188 deletions.

This file was deleted.

This file was deleted.

This file was deleted.

13 changes: 13 additions & 0 deletions example/.idea/runConfigurations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.lifecycle.coroutineScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.spruceid.mobile.sdk.oid4vp.CredentialPresentation
import com.spruceid.mobile.sdk.oid4vp.OID4VPHolder
import com.spruceid.mobile.sdk.rs.Credential
import com.spruceid.mobilesdkexample.db.AppDatabase
import com.spruceid.mobilesdkexample.db.RawCredentialsRepository
import com.spruceid.mobilesdkexample.navigation.SetupNavGraph
Expand All @@ -24,6 +22,9 @@ import com.spruceid.mobilesdkexample.ui.theme.MobileSdkTheme
import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel
import com.spruceid.mobilesdkexample.viewmodels.RawCredentialsViewModelFactory
import kotlinx.coroutines.launch
import com.spruceid.mobile.sdk.KeyManager

const val DEFAULT_KEY_ID = "key-1"

class MainActivity : ComponentActivity() {
private lateinit var navController: NavHostController
Expand All @@ -33,8 +34,9 @@ class MainActivity : ComponentActivity() {

val deepLinkUri: Uri? = intent.data
if (deepLinkUri != null) {
// Remove? TBD
if (deepLinkUri.scheme == "oid4vp://") {
// NOTE: See ScanOID4VPQR.kt for handling OID4VP QR code scanning,
// NOTE: See DispatchQRView.kt for handling OID4VP QR code scanning,
// and credential selection.
}
}
Expand All @@ -52,6 +54,32 @@ class MainActivity : ComponentActivity() {
val credentialsViewModel: IRawCredentialsViewModel by viewModels {
RawCredentialsViewModelFactory((application as MainApplication).rawCredentialsRepository)
}

// Insert a raw credential into the rawCredentialsRepository,
// using a suspend / async method.
LaunchedEffect(credentialsViewModel) {
lifecycle.coroutineScope.launch {
// Setup a default keyId for the RequestSigner.
// Check the key manager if the key exists, if not, create it.
val km = KeyManager()

if (!km.keyExists(DEFAULT_KEY_ID)) {
// Key does not exist, create it.
km.generateSigningKey(DEFAULT_KEY_ID)
}


// Clear the raw credentials table.
// credentialsViewModel.deleteAllRawCredentials()
// // Load the exampleSdJwt into the raw credentials table.
// credentialsViewModel.saveRawCredential(
// com.spruceid.mobilesdkexample.db.RawCredentials(
// rawCredential = exampleSdJwt
// )
// )
}
}

SetupNavGraph(navController, credentialsViewModel)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const val VERIFY_VC_PATH = "verify_vc"
const val VERIFIER_SETTINGS_HOME_PATH = "verifier_settings_home"
const val WALLET_SETTINGS_HOME_PATH = "wallet_settings_home"
const val ADD_TO_WALLET_PATH = "add_to_wallet/{rawCredential}"
const val OID4VP_PATH = "oid4vp/{params}"
const val SCAN_QR_PATH = "scan_qr"
const val HANDLE_OID4VP_PATH = "oid4vp/{url}"


sealed class Screen(val route: String) {
Expand All @@ -18,5 +19,6 @@ sealed class Screen(val route: String) {
object VerifierSettingsHomeScreen : Screen(VERIFIER_SETTINGS_HOME_PATH)
object WalletSettingsHomeScreen : Screen(WALLET_SETTINGS_HOME_PATH)
object AddToWalletScreen : Screen(ADD_TO_WALLET_PATH)
object ScanQRScreen : Screen(OID4VP_PATH)
object ScanQRScreen : Screen(SCAN_QR_PATH)
object HandleOID4VP : Screen(HANDLE_OID4VP_PATH)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.spruceid.mobilesdkexample.navigation

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
Expand All @@ -15,6 +14,7 @@ import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel
import com.spruceid.mobilesdkexample.wallet.AddToWalletView
import com.spruceid.mobilesdkexample.walletsettings.WalletSettingsHomeView
import com.spruceid.mobilesdkexample.wallet.DispatchQRView
import com.spruceid.mobilesdkexample.wallet.HandleOID4VPView

@Composable
fun SetupNavGraph(
Expand Down Expand Up @@ -68,13 +68,19 @@ fun SetupNavGraph(
}
composable(
route = Screen.ScanQRScreen.route,
) {
DispatchQRView(navController)
}
composable(
route = Screen.HandleOID4VP.route,
deepLinks = listOf(
navDeepLink {
uriPattern = "oid4vp://{params}"
uriPattern = "oid4vp://{url}"
}
)
) {
DispatchQRView(navController)
) { backStackEntry ->
val url = backStackEntry.arguments?.getString("url")!!
HandleOID4VPView(navController, rawCredentialsViewModel, url)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ val keyPEM = "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHB


val keyBase64 =
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEAqKZdZQgPVtjlEBfz2ItHG8oXIONenOxRePtqOQ42yhRANCAATA43gI2Ib8+qKK4YEOfNCRiNOhyHaCLgAvKdhHS+y6wpG3oJ2xudXagzKKbcfvUda4x0j8zR1/oD56mpm85GbO"
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEAqKZdZQgPVtjlEBfz2ItHG8oXIONenOxRePtqOQ42yhRANCAATA43gI2Ib8+qKK4YEOfNCRiNOhyHaCLgAvKdhHS+y6wpG3oJ2xudXagzKKbcfvUda4x0j8zR1/oD56mpm85GbO"

//const val exampleSdJwt = "eyJhbGciOiJFUzI1NiJ9.eyJfc2RfYWxnIjoic2hhMjU2IiwiY3JlZGVudGlhbFN1YmplY3QiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnL25zL2NyZWRlbnRpYWxzL3YyIiwiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9vYi92M3AwL2NvbnRleHQtMy4wLjMuanNvbiJdLCJhd2FyZGVkRGF0ZSI6IjIwMjQtMDktMjNUMTg6MTI6MTIrMDAwMCIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkZW50aXR5IjpbeyJoYXNoZWQiOmZhbHNlLCJpZGVudGl0eUhhc2giOiJKb2huIFNtaXRoIiwiaWRlbnRpdHlUeXBlIjoibmFtZSIsInNhbHQiOiJub3QtdXNlZCIsInR5cGUiOiJJZGVudGl0eU9iamVjdCJ9LHsiaGFzaGVkIjpmYWxzZSwiaWRlbnRpdHlIYXNoIjoiam9obi5zbWl0aEBleGFtcGxlLmNvbSIsImlkZW50aXR5VHlwZSI6ImVtYWlsQWRkcmVzcyIsInNhbHQiOiJub3QtdXNlZCIsInR5cGUiOiJJZGVudGl0eU9iamVjdCJ9XSwiYWNoaWV2ZW1lbnQiOnsibmFtZSI6IkNvbG9yYWRvRldEIFRlYW0gTWVtYmVyc2hpcCIsInR5cGUiOiJBY2hpZXZlbWVudCJ9fSwiaXNzdWVyIjp7ImlkIjoiZGlkOmp3azpleUpoYkdjaU9pSkZVekkxTmlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpYTNSNUlqb2lSVU1pTENKNElqb2liV0pVTTJkcU9XRnZPR051UzI4ME0wcHJjVlJQVW1OSlFWSTRNRmd3VFVGWFFXTkdZelp2UjFKTVl5SXNJbmtpT2lKaU9GVk9ZMGhETW1GSFEzSjFTVFowUWxSV1NWWTBkVzVaV0VWeVMwTTRaRFJuUlRGR1owczBRMDVKSW4wIzAiLCJuYW1lIjoiQ29sb3JhZG8gV29ya2ZvcmNlIERldmVsb3BtZW50IENvdW5jaWwiLCJ0eXBlIjoiUHJvZmlsZSJ9LCJuYW1lIjoiQ29sb3JhZG9GV0RUZWFtTWVtYmVyc2hpcCIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJPcGVuQmFkZ2VDcmVkZW50aWFsIl19fQ.Aj40fUgmPygWVULtPgVWbpIsgbZjBp-KE_ZudRYpZ90PPGSbRf3hLEHZ9nkCKhYX7xCmTOtdYHnjhEwIFN1Xng~";

const val exampleSdJwt = "eyJhbGciOiJFZERTQSJ9.eyJfc2RfYWxnIjoic2hhLTI1NiIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwic3ViIjoiMTIzNDU2Nzg5MCIsIl9zZCI6WyJ4U1poOVJaU0RQOEhnNXllZ3Vlck9TbnZ2UXFDcjF4RmdQc1g4U1A4Qkk4Il19.5rxobR0a1hT8WUCN86p8rbQuUsBEcLLuE7p6UBSfAzCgbaF0ZxbXcnAPPwGnBQVcETx7QAmxCJNudfQ5n4tADg~WyIyVmpiY3FEd3RtdTNiWHlZaUJIbjZRIiwidmMiLHsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnL25zL2NyZWRlbnRpYWxzL3YyIiwiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9vYi92M3AwL2NvbnRleHQtMy4wLjMuanNvbiJdLCJhd2FyZGVkRGF0ZSI6IjIwMjQtMDktMjNUMTg6MTI6MTIrMDAwMCIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkZW50aXR5IjpbeyJoYXNoZWQiOmZhbHNlLCJpZGVudGl0eUhhc2giOiJKb2huIFNtaXRoIiwiaWRlbnRpdHlUeXBlIjoibmFtZSIsInNhbHQiOiJub3QtdXNlZCIsInR5cGUiOiJJZGVudGl0eU9iamVjdCJ9LHsiaGFzaGVkIjpmYWxzZSwiaWRlbnRpdHlIYXNoIjoiam9obi5zbWl0aEBleGFtcGxlLmNvbSIsImlkZW50aXR5VHlwZSI6ImVtYWlsQWRkcmVzcyIsInNhbHQiOiJub3QtdXNlZCIsInR5cGUiOiJJZGVudGl0eU9iamVjdCJ9XSwiYWNoaWV2ZW1lbnQiOnsibmFtZSI6IkNvbG9yYWRvRldEIFRlYW0gTWVtYmVyc2hpcCIsInR5cGUiOiJBY2hpZXZlbWVudCJ9fSwiaXNzdWVyIjp7ImlkIjoiZGlkOmp3azpleUpoYkdjaU9pSkZVekkxTmlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpYTNSNUlqb2lSVU1pTENKNElqb2liV0pVTTJkcU9XRnZPR051UzI4ME0wcHJjVlJQVW1OSlFWSTRNRmd3VFVGWFFXTkdZelp2UjFKTVl5SXNJbmtpT2lKaU9GVk9ZMGhETW1GSFEzSjFTVFowUWxSV1NWWTBkVzVaV0VWeVMwTTRaRFJuUlRGR1owczBRMDVKSW4wIzAiLCJuYW1lIjoiQ29sb3JhZG8gV29ya2ZvcmNlIERldmVsb3BtZW50IENvdW5jaWwiLCJ0eXBlIjoiUHJvZmlsZSJ9LCJuYW1lIjoiQ29sb3JhZG9GV0RUZWFtTWVtYmVyc2hpcCIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJPcGVuQmFkZ2VDcmVkZW50aWFsIl19XQ~"

Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class AchievementCredentialItem {

constructor(rawCredential: String, onDelete: (() -> Unit)? = null) {
val decodedSdJwt = decodeRevealSdJwt(rawCredential)

this.credential = JSONObject(decodedSdJwt)
this.onDelete = onDelete
}
Expand Down Expand Up @@ -244,12 +245,15 @@ class AchievementCredentialItem {

@Composable
fun detailsComponent() {
val awardedDate = keyPathFinder(credential, mutableListOf("awardedDate")).toString()
println("credential: $credential")
// NOTE: The credential contains a `vc` property with the verifiable credential payload.
val awardedDate = keyPathFinder(credential, mutableListOf("vc", "awardedDate")).toString()
println("awardedDate: $awardedDate")
val ISO8601DateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]Z")
val parsedDate = OffsetDateTime.parse(awardedDate, ISO8601DateFormat)
val dateTimeFormatter = DateTimeFormatter.ofPattern("MMM dd, yyyy 'at' h:mm a")

val identity = keyPathFinder(credential, mutableListOf("credentialSubject", "identity")) as JSONArray
val identity = keyPathFinder(credential, mutableListOf("vc", "credentialSubject", "identity")) as JSONArray
val details = MutableList(identity.length()) { i ->
val obj = identity.get(i) as JSONObject
Pair(obj["identityType"].toString(), obj["identityHash"].toString())
Expand Down
Loading

0 comments on commit 1e47f6a

Please sign in to comment.