stateofca / opencred

OpenCred Verifiable Credentials Platform
BSD 3-Clause "New" or "Revised" License
25 stars 3 forks source link

How to test this implementation? CA DMV doesn't seem to work #4

Closed tmarkovski closed 3 weeks ago

tmarkovski commented 1 month ago

I got the service up and running, proxying to a public ngrok address, and I'm attempting a verify workflow. I got the QR code (or URL) generated from the /context/login endpoint and I scan this with an iPhone that does have CA mDL for that user. Upon scanning the code CA DMV app launches correctly, and I can see that it makes two distinct calls to my ngrok service

GET /.well-known/did.json                                                                                        200 OK               
GET /workflows/z1A32xJZGqBeAEcMq56avmw2L/exchanges/z1AECzHfWSaqPJjPMDGnHUT13/openid/client/authorization/request 200 OK 

However, nothing happens after this. Neither the app, nor my running service report any error. I have configured my opencred service with the default examples shown in the readme, and it seems they're getting processed, but I have no way to diagnose why it won't complete the flow. Any way we can work through this using some test harness or a development app?

mattcollier commented 1 month ago

@tmarkovski Can you provide a copy of the YAML configuration you're using, of course redacting any secrets/URLs you don't want to expose?

tmarkovski commented 1 month ago

Sure. Any keys are copied from the README, so nothing sensitive.

Configuration ```yaml # Copyright 2023 - 2024 California Department of Motor Vehicles # Copyright 2023 - 2024 Digital Bazaar, Inc. # # SPDX-License-Identifier: BSD-3-Clause app: server: baseUri: https://trinsic.ngrok.io opencred: caStore: - pem: | -----BEGIN CERTIFICATE----- -----END CERTIFICATE----- signingKeys: - type: ES256 id: 91705ba8b54357e00953b2d5cc2d805c25f86bbec4777ea4f0dc883dd84b4803 privateKeyPem: | -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgdU1KX0SdMjy4AzVm 5awy7B3tHz0y+mckq/x2V8fWwrmhRANCAARkJ4rsoMcdayGPTcAbgLfKRdqwN57I n9CRsED9Yno+oC4R7xz6xXpT2CQAkioPDmou1DYYU+oMaV9lCjvw9vqs -----END PRIVATE KEY----- publicKeyPem: | -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZCeK7KDHHWshj03AG4C3ykXasDee yJ/QkbBA/WJ6PqAuEe8c+sV6U9gkAJIqDw5qLtQ2GFPqDGlfZQo78Pb6rA== -----END PUBLIC KEY----- purpose: - id_token - authorization_request - type: ES256 id: 9e2da25d9f63385ab202906e2c2fb007ddce9848219a39465bccfd254461e9da privateKeyPem: | -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzv8CNRomqzjgoY+e lfvgwYmF/mCjB9lWWRtpBRebfrShRANCAAQwkHHnOQPp19F7UELqQHGl6bX737BR gL81vPeyyi2v9plDCkcuzI+eu0jD5am0Ju4M0ay6XK5ksNrE80Wywt1R -----END PRIVATE KEY----- publicKeyPem: | -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMJBx5zkD6dfRe1BC6kBxpem1+9+w UYC/Nbz3ssotr/aZQwpHLsyPnrtIw+WptCbuDNGsulyuZLDaxPNFssLdUQ== -----END PUBLIC KEY----- purpose: - authentication - assertionMethod - authorization_request relyingParties: - name: "Test Relying Party (Native)" clientId: "rp1" clientSecret: "DeepestAndDarkest" description: "Relying Party Test App" primaryLogo: "https://placekitten.com/200/200" primaryLink: "https://example.com" secondaryLogo: "https://placekitten.com/200/200" secondaryLink: "https://example2.com" homeLink: "https://example.com" explainerVideo: id: "" provider: youtube backgroundImage: "https://placekitten.com/800/200" redirectUri: "http://localhost:3000" idTokenExpirySeconds: 3600 claims: - name: "dl_number" path: "driversLicense.document_number" brand: cta: "#0B669D" primary: "#045199" header: "#0979c4" scopes: - name: "openid" description: "Open ID Connect" workflow: type: native id: z1A32xJZGqBeAEcMq56avmw2L initialStep: default steps: default: createChallenge: true verifiablePresentationRequest: > { "query": { "type": "QueryByExample", "credentialQuery": { "reason": "Please present your VC.", "example": { "type": [ "Iso18013DriversLicenseCredential" ] } } } } constraintsOverride: > { "fields": [ { "path": [ "$.vc.type" ], "filter": { "type": "string", "pattern": "Iso18013DriversLicenseCredential" } } ] } - name: "Test App" # An example that uses a remove VC-API exchanger to connect with a wallet # See Verifiable Credentials API Documentation: https://w3c-ccg.github.io/vc-api/ clientId: "32bb6cec384b" clientSecret: "DeepestAndDarkest" description: "Relying Party Test App" icon: "https://placekitten.com/200/200" backgroundImage: "https://placekitten.com/800/300" idTokenExpirySeconds: 3600 brand: cta: "#0B669D" primary: "#045199" header: "#0979c4" redirectUri: "http://localhost:3000" scopes: - name: "openid" description: "Open ID Connect" claims: - name: "email" path: "email" workflow: type: vc-api baseUrl: https://example.org/exchanges/123 capability: "example" clientSecret: "example" - client_id: 4e3a8b7b4b7b4b7b4b7b # Microsoft Entra Verified Id workflow config # Many of these config fields map to request fields in the APIs linked below: # https://learn.microsoft.com/en-us/entra/verified-id/get-started-request-api # https://learn.microsoft.com/en-us/entra/verified-id/presentation-request-api configFrom: 32bb6cec384b # client_id of another relying party to serve as default values workflow: type: microsoft-entra-verified-id apiBaseUrl: https://api.entra.microsoft.example.com/v1.0 # required (will generally be https://verifiedid.did.msidentity.com/v1.0) apiLoginBaseUrl: https://login.entra.microsoft.example.com # required (will generally be https://login.microsoftonline.com) apiClientId: "TODO" # required apiClientSecret: "TODO" # required apiTenantId: "TODO" # required verifierDid: "TODO" # required verifierName: "TODO" # required acceptedCredentialType: "TODO" # required credentialVerificationCallbackAuthEnabled: true # optional (default populated in middleware unless overridden here) acceptedCredentialIssuers: [] # optional (default populated in middleware unless overridden here) credentialVerificationPurpose: "So that we can evaluate your permission to access the requested resource" # optional (default populated in middleware unless overridden here) allowRevokedCredentials: false # optional (default populated in middleware unless overridden here) validateLinkedDomain: false # optional (default populated in middleware unless overridden here) includeQrCode: true # optional (default populated in middleware unless overridden here) - name: "load-test" clientId: "load-test" clientSecret: "DeepestAndDarkest" description: "Load Testing Profile" icon: "https://imagedelivery.net/I-hc6FAYxquPgv-npvTcWQ/241cb0c9-d92c-40b7-1745-6d5e68a45d00/public" backgroundImage: "https://imagedelivery.net/I-hc6FAYxquPgv-npvTcWQ/f7516576-f9d3-4a15-1b9e-e95281613700/public" redirectUri: "http://localhost:3000" idTokenExpirySeconds: 3600 claims: - name: "dl_number" path: "driversLicense.document_number" brand: cta: "#0B669D" primary: "#045199" header: "#0979c4" scopes: - name: "openid" description: "Open ID Connect" workflow: type: native id: load-test initialStep: default steps: default: createChallenge: true verifiablePresentationRequest: > { "query": { "type": "QueryByExample", "credentialQuery": { "reason": "Please present your VC.", "example": { "type": [ "DomainLinkageCredential" ] } } } } constraintsOverride: > { "fields": [ { "path": [ "$.vc.type" ], "filter": { "type": "string", "pattern": "DomainLinkageCredential" } } ] } options: exchangeProtocols: - chapi - openid4vp defaultBrand: cta: "#0B669D" primary: "#045199" header: "#0979c4" enableAudit: true auditFields: - type: text id: given_name name: First Name path: "$.credentialSubject.given_name" required: true - type: text id: family_name name: Last Name path: "$.credentialSubject.family_name" required: false - type: date id: birth_date name: Date of Birth path: "$.credentialSubject.birth_date" required: true defaultLanguage: en translations: en: qrTitle: Login with your Wallet app qrPageExplain: Scan the following QR Code using the Wallet app on your phone. qrPageExplainHelp: (How do I do it?) qrFooter: "Note: Already on your phone with the Wallet app? Open the Wallet app, then come back and tap on the QR code above." qrFooterHelp: Difficulty using the Wallet app to login? revert to using password here qrDisclaimer: If you don't have a Wallet app download it from the app store. qrExplainerText: (How do I do it?) qrPageAnotherWay: Want to try another way? chapiPageAnotherWay: "Looking for a QR Code to scan with you wallet app instead?" loginCta: "Login with your credential wallet" loginExplain: "To login with your credential wallet, you will need to have the credential wallet app installed" appInstallExplain: "If you don't have a credential wallet yet, you can get one by downloading the credential wallet app " appCta: "Open wallet app" copyright: "Powered by OpenCred" pageTitle: "Login" home: "Home" didWeb: mainEnabled: true linkageEnabled: true mainDocument: > { "id": "did:web:example.com", "@context": [ "https://www.w3.org/ns/did/v1", { "@base": "did:web:example.com" } ], "service": [ { "id": "#linkeddomains", "type": "LinkedDomains", "serviceEndpoint": { "origins": [ "https://example.com" ] } }, { "id": "#hub", "type": "IdentityHub", "serviceEndpoint": { "instances": [ "https://hub.did.msidentity.com/v1.0/test-instance-id" ] } } ], "verificationMethod": [ { "id": "test-signing-key", "controller": "did:web:example.com", "type": "EcdsaSecp256k1VerificationKey2019", "publicKeyJwk": { "crv": "secp256k1", "kty": "EC", "x": "test-x", "y": "test-y" } } ], "authentication": [ "test-signing-key" ], "assertionMethod": [ "test-signing-key" ] } linkageDocument: > { "@context": "https://identity.foundation/.well-known/did-configuration/v1", "linked_dids": ["eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa29USHNnTk5yYnk4SnpDTlExaVJMeVc1UVE2UjhYdXU2QUE4aWdHck1WUFVNI3o2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSJ9.eyJleHAiOjE3NjQ4NzkxMzksImlzcyI6ImRpZDprZXk6ejZNa29USHNnTk5yYnk4SnpDTlExaVJMeVc1UVE2UjhYdXU2QUE4aWdHck1WUFVNIiwibmJmIjoxNjA3MTEyNzM5LCJzdWIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9pZGVudGl0eS5mb3VuZGF0aW9uLy53ZWxsLWtub3duL2RpZC1jb25maWd1cmF0aW9uL3YxIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1rb1RIc2dOTnJieThKekNOUTFpUkx5VzVRUTZSOFh1dTZBQThpZ0dyTVZQVU0iLCJvcmlnaW4iOiJpZGVudGl0eS5mb3VuZGF0aW9uIn0sImV4cGlyYXRpb25EYXRlIjoiMjAyNS0xMi0wNFQxNDoxMjoxOS0wNjowMCIsImlzc3VhbmNlRGF0ZSI6IjIwMjAtMTItMDRUMTQ6MTI6MTktMDY6MDAiLCJpc3N1ZXIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJEb21haW5MaW5rYWdlQ3JlZGVudGlhbCJdfX0.aUFNReA4R5rcX_oYm3sPXqWtso_gjPHnWZsB6pWcGv6m3K8-4JIAvFov3ZTM8HxPOrOL17Qf4vBFdY9oK0HeCQ"] } ```
mattcollier commented 1 month ago

And to be clear, we're talking about the relyingParty definition that has a workflow.id of z1A32xJZGqBeAEcMq56avmw2L correct?

tmarkovski commented 1 month ago

Correct.

mattcollier commented 1 month ago

@tmarkovski would you be willing to remove the RPs that you're not using from the above example config and test with that to setup a minimum runnable example? Along with updating the config in your earlier post?

tmarkovski commented 1 month ago

Sure. Here's the latest update. This resulted in the same behavior.

Edit: I'm supplying the config through an env var, if that makes any difference. I can see that it picks up on changes.

config.yaml ```yaml # Copyright 2023 - 2024 California Department of Motor Vehicles # Copyright 2023 - 2024 Digital Bazaar, Inc. # # SPDX-License-Identifier: BSD-3-Clause app: server: baseUri: https://trinsic.ngrok.io opencred: caStore: - pem: | -----BEGIN CERTIFICATE----- -----END CERTIFICATE----- signingKeys: - type: ES256 id: 91705ba8b54357e00953b2d5cc2d805c25f86bbec4777ea4f0dc883dd84b4803 privateKeyPem: | -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgdU1KX0SdMjy4AzVm 5awy7B3tHz0y+mckq/x2V8fWwrmhRANCAARkJ4rsoMcdayGPTcAbgLfKRdqwN57I n9CRsED9Yno+oC4R7xz6xXpT2CQAkioPDmou1DYYU+oMaV9lCjvw9vqs -----END PRIVATE KEY----- publicKeyPem: | -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZCeK7KDHHWshj03AG4C3ykXasDee yJ/QkbBA/WJ6PqAuEe8c+sV6U9gkAJIqDw5qLtQ2GFPqDGlfZQo78Pb6rA== -----END PUBLIC KEY----- purpose: - id_token - authorization_request - type: ES256 id: 9e2da25d9f63385ab202906e2c2fb007ddce9848219a39465bccfd254461e9da privateKeyPem: | -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzv8CNRomqzjgoY+e lfvgwYmF/mCjB9lWWRtpBRebfrShRANCAAQwkHHnOQPp19F7UELqQHGl6bX737BR gL81vPeyyi2v9plDCkcuzI+eu0jD5am0Ju4M0ay6XK5ksNrE80Wywt1R -----END PRIVATE KEY----- publicKeyPem: | -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMJBx5zkD6dfRe1BC6kBxpem1+9+w UYC/Nbz3ssotr/aZQwpHLsyPnrtIw+WptCbuDNGsulyuZLDaxPNFssLdUQ== -----END PUBLIC KEY----- purpose: - authentication - assertionMethod - authorization_request relyingParties: - name: "Test Relying Party (Native)" clientId: "rp1" clientSecret: "DeepestAndDarkest" description: "Relying Party Test App" primaryLogo: "https://placekitten.com/200/200" primaryLink: "https://example.com" secondaryLogo: "https://placekitten.com/200/200" secondaryLink: "https://example2.com" homeLink: "https://example.com" explainerVideo: id: "" provider: youtube backgroundImage: "https://placekitten.com/800/200" redirectUri: "http://localhost:3000" idTokenExpirySeconds: 3600 claims: - name: "dl_number" path: "driversLicense.document_number" brand: cta: "#0B669D" primary: "#045199" header: "#0979c4" scopes: - name: "openid" description: "Open ID Connect" workflow: type: native id: z1A32xJZGqBeAEcMq56avmw2L initialStep: default steps: default: createChallenge: true verifiablePresentationRequest: > { "query": { "type": "QueryByExample", "credentialQuery": { "reason": "Please present your VC.", "example": { "type": [ "Iso18013DriversLicenseCredential" ] } } } } constraintsOverride: > { "fields": [ { "path": [ "$.vc.type" ], "filter": { "type": "string", "pattern": "Iso18013DriversLicenseCredential" } } ] } options: exchangeProtocols: - chapi - openid4vp defaultBrand: cta: "#0B669D" primary: "#045199" header: "#0979c4" enableAudit: true auditFields: - type: text id: given_name name: First Name path: "$.credentialSubject.given_name" required: true - type: text id: family_name name: Last Name path: "$.credentialSubject.family_name" required: false - type: date id: birth_date name: Date of Birth path: "$.credentialSubject.birth_date" required: true defaultLanguage: en translations: en: qrTitle: Login with your Wallet app qrPageExplain: Scan the following QR Code using the Wallet app on your phone. qrPageExplainHelp: (How do I do it?) qrFooter: "Note: Already on your phone with the Wallet app? Open the Wallet app, then come back and tap on the QR code above." qrFooterHelp: Difficulty using the Wallet app to login? revert to using password here qrDisclaimer: If you don't have a Wallet app download it from the app store. qrExplainerText: (How do I do it?) qrPageAnotherWay: Want to try another way? chapiPageAnotherWay: "Looking for a QR Code to scan with you wallet app instead?" loginCta: "Login with your credential wallet" loginExplain: "To login with your credential wallet, you will need to have the credential wallet app installed" appInstallExplain: "If you don't have a credential wallet yet, you can get one by downloading the credential wallet app " appCta: "Open wallet app" copyright: "Powered by OpenCred" pageTitle: "Login" home: "Home" didWeb: mainEnabled: true linkageEnabled: true mainDocument: > { "id": "did:web:trinsic.ngrok.io", "@context": [ "https://www.w3.org/ns/did/v1", { "@base": "did:web:trinsic.ngrok.io" } ], "service": [ { "id": "#linkeddomains", "type": "LinkedDomains", "serviceEndpoint": { "origins": [ "https://example.com" ] } }, { "id": "#hub", "type": "IdentityHub", "serviceEndpoint": { "instances": [ "https://hub.did.msidentity.com/v1.0/test-instance-id" ] } } ], "verificationMethod": [ { "id": "test-signing-key", "controller": "did:web:trinsic.ngrok.io", "type": "EcdsaSecp256k1VerificationKey2019", "publicKeyJwk": { "crv": "secp256k1", "kty": "EC", "x": "test-x", "y": "test-y" } } ], "authentication": [ "test-signing-key" ], "assertionMethod": [ "test-signing-key" ] } linkageDocument: > { "@context": "https://identity.foundation/.well-known/did-configuration/v1", "linked_dids": ["eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa29USHNnTk5yYnk4SnpDTlExaVJMeVc1UVE2UjhYdXU2QUE4aWdHck1WUFVNI3o2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSJ9.eyJleHAiOjE3NjQ4NzkxMzksImlzcyI6ImRpZDprZXk6ejZNa29USHNnTk5yYnk4SnpDTlExaVJMeVc1UVE2UjhYdXU2QUE4aWdHck1WUFVNIiwibmJmIjoxNjA3MTEyNzM5LCJzdWIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9pZGVudGl0eS5mb3VuZGF0aW9uLy53ZWxsLWtub3duL2RpZC1jb25maWd1cmF0aW9uL3YxIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1rb1RIc2dOTnJieThKekNOUTFpUkx5VzVRUTZSOFh1dTZBQThpZ0dyTVZQVU0iLCJvcmlnaW4iOiJpZGVudGl0eS5mb3VuZGF0aW9uIn0sImV4cGlyYXRpb25EYXRlIjoiMjAyNS0xMi0wNFQxNDoxMjoxOS0wNjowMCIsImlzc3VhbmNlRGF0ZSI6IjIwMjAtMTItMDRUMTQ6MTI6MTktMDY6MDAiLCJpc3N1ZXIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJEb21haW5MaW5rYWdlQ3JlZGVudGlhbCJdfX0.aUFNReA4R5rcX_oYm3sPXqWtso_gjPHnWZsB6pWcGv6m3K8-4JIAvFov3ZTM8HxPOrOL17Qf4vBFdY9oK0HeCQ"] } ```
GET /context/login ``` curl -X 'GET' \ 'http://localhost:22080/context/login?client_id=rp1&redirect_uri=http%3A%2F%2Flocalhost%3A3000&scope=openid&response_type=code' \ -H 'accept: text/html' ``` ``` { "step": "login", "rp": { "clientId": "rp1", "redirectUri": "http://localhost:3000", "name": "Test Relying Party (Native)", "primaryLogo": "https://placekitten.com/200/200", "primaryLink": "https://example.com", "secondaryLogo": "https://placekitten.com/200/200", "secondaryLink": "https://example2.com", "homeLink": "https://example.com", "brand": { "cta": "#0B669D", "primary": "#045199", "header": "#0979c4" }, "backgroundImage": "https://placekitten.com/800/200", "explainerVideo": { "id": "", "provider": "youtube" }, "workflow": { "type": "native", "id": "z1A32xJZGqBeAEcMq56avmw2L" } }, "options": { "exchangeProtocols": [ "chapi", "openid4vp" ] }, "exchangeData": { "id": "z19w7AfgEh9GoVfeeUBU9z45x", "vcapi": "https://trinsic.ngrok.io/workflows/z1A32xJZGqBeAEcMq56avmw2L/exchanges/z19w7AfgEh9GoVfeeUBU9z45x", "OID4VP": "openid4vp://?client_id=did%3Aweb%3Atrinsic.ngrok.io&request_uri=https%3A%2F%2Ftrinsic.ngrok.io%2Fworkflows%2Fz1A32xJZGqBeAEcMq56avmw2L%2Fexchanges%2Fz19w7AfgEh9GoVfeeUBU9z45x%2Fopenid%2Fclient%2Fauthorization%2Frequest", "accessToken": "z1AGPh1UaawNyqxcQbBi5EQX3", "workflowId": "z1A32xJZGqBeAEcMq56avmw2L", "oidc": { "code": null, "state": "" }, "QR": "data:image/png;base64,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" } } ```
deshmukhrajvardhan commented 1 month ago

@tmarkovski I was able to reproduce https://github.com/stateofca/opencred/issues/4#issuecomment-2145496704 Is https://trinsic.ngrok.io an RP that has implemented the diagram mentioned in the README? Also, how do you render the QR code and other data, does this repo have those tools are is it your custom implementation? Thanks for creating this issue.

tmarkovski commented 1 month ago

Thanks for looking into it @deshmukhrajvardhan I do not have a website that acts as an RP, I simply use ngrok (instead localtunnel from the README), so I can have publicly available service for the mobile app - I'm only trying to get the service running. However I do not think this is necessary to get a response from the wallet app.

This is how I crated my setup.

The QR code is contained in the response of the GET /context/login request that I make. I understand the QR code is just an encoded URL of the data in the exchangeData.OID4VP field. I simply show this data in a browser. Once I scan it with the CA DMV wallet, I can see that the wallet makes two requests in my ngrok interface, as shown in my original comment. When the mobile app calls /openid/client/authorization/request in the response of that there is another URL which points to /openid/client/authorization/response with the domain trinsic.ngrok.io. I'm expecting the app to prepare and submit response at this URL. The app does not seem to process this part.

kezike commented 1 month ago

Hi @tmarkovski - what do you see when you visit https://trinsic.ngrok.io/login?client_id=rp1&redirect_uri=http%3A%2F%2Flocalhost%3A3000&state=whateveryouwant&scope=openid?

tmarkovski commented 1 month ago

I see the web app interface offering me to login using CHAPI or scan QR code. Scanning this QR code results in the same behavior.

image
kezike commented 1 month ago

Could you give more information on how you obtained your DL into your CA DMV wallet?

tmarkovski commented 1 month ago

My team members from CA that are helping me test will respond shortly.

Is this setup working for you @kezike ? I've also left my public bridge up, so you can try for yourself if you have a CA DL.

Additionally, is there a config file for development that you would recommend I try?

janpieterz commented 1 month ago

@kezike I basically went through the default onboarding flow the CA DMV app provides us. That is the CA DMV Wallet app (I know there are two). This video shows the onboarding process.

michaeldboyd commented 1 month ago

I also went through the default onboarding flow

ottonomy commented 1 month ago

@kezike I have now also reproduced a variation of this problem.

Next steps tomorrow include: getting access to the iOS error log by hooking the phone up to the computer to see if there are any more informative errors in there.

Using a relying party configured in combined.yaml like the following:

      - name: Utopia DMV
        clientId: "dmv"
        clientSecret: "toosecrettopastehere"
        description: "Utopia DMV"
        icon: ""
        backgroundImage: ""
        brand:
          cta: "#0B669D"
          primary: "#045199"
          header: "#0979c4"
        redirectUri: "http://localhost:3000"
        explainerVideo:
          provider: "youtube"
          id: "dQw4w9WgXcQ"
        scopes:
          - name: "openid"
            description: "Open ID Connect"
        workflow: 
          type: native
          id: default
          initialStep: default
          steps:
            default:
              createChallenge: true
              verifiablePresentationRequest: >
                {
                  "query": {
                    "type": "QueryByExample",
                    "credentialQuery": {
                      "reason": "Please present your Driver's License to complete the verification process.",
                      "example": {
                        "@context": [
                          "https://www.w3.org/2018/credentials/v1",
                          "https://w3id.org/vdl/v1",
                          "https://w3id.org/vdl/aamva/v1"
                        ],
                        "type": [
                          "Iso18013DriversLicenseCredential"
                        ]
                      }
                    }
                  }
                }
ottonomy commented 1 month ago

Update after a little more research today.

I am still not able to configure a successful local w/ngrok tunnel connection to the wallet. I got to a point where the presentation_definition would generate the same input_descriptors as the working UAT OpenCred environment.

That is possible by changing the above workflow to this, with constraintsOverride:

        workflow: 
          type: native
          id: dmvappworkflow
          initialStep: default
          steps:
            default:
              createChallenge: true
              verifiablePresentationRequest: >
                {
                  "query": {
                    "type": "QueryByExample",
                    "credentialQuery": {
                      "reason": "Please present your Driver's License to complete the verification process.",
                      "example": {
                        "@context": [
                          "https://www.w3.org/2018/credentials/v1",
                          "https://w3id.org/vdl/v1",
                          "https://w3id.org/vdl/aamva/v1"
                        ],
                        "type": [
                          "Iso18013DriversLicenseCredential"
                        ]
                      }
                    }
                  }
                }
              constraintsOverride: >
                {
                  "fields": [
                    {
                      "path": [
                        "$.vc.type"
                      ],
                      "filter": {
                        "type": "string",
                        "pattern": "Iso18013DriversLicenseCredential"
                      }
                    }
                  ]
                }

but I still get the same behavior in the wallet, which is once I scan the QR code, the wallet makes a request to the request_uri in the QR code, and the wallet then happily to the Home tab with no indication of failure.

I did proceed to connect my iPhone to my MacOS device to try to capture logs with the Console app. I successfully saw a bunch of logs from the Springboard process and others related to com.spruceid.app.credible.mdl, but no log messages error or otherwise seemed to relate to anything that might have gone wrong processing the JWT retrieved from the server.

Then I noticed in my ngrok logs a request for the DID document, which was disabled on my server. I had the wrong config key for it. Incorrect configuration:

app:
  opencred:
    didWeb:
      enabled: true

Corrected, this should be:

app:
  opencred:
    didWeb:
      mainEnabled: true

This was SUCCESSFUL. The wallet prompted me to share the credential, and it worked.

Note I didn't define a mainDocument statically, I let it be generated dynamically. Probably the "id": "did:web:example.com" should have been "id": "did:web:trinsic.ngrok.io" in your config @tmarkovski. My guess is that this was an issue processing the DID Web document and the signature on the JWT discovered from the request_uri in the QR code. The wallet really should be displaying a human readable error in this case, but hopefully you'll be able to work around that with this guidance. Go ahead and close the issue if it works.

tmarkovski commented 3 weeks ago

Thank you @ottonomy. The didWeb configuration was indeed the issue. We managed to get a successful presentation up. Thank you (and everyone who contributed to this issue) for looking into this and helping us get the POC up and running.

deshmukhrajvardhan commented 3 weeks ago

@tmarkovski I was able to get a local setup where the mobile phone can reach the HTTP endpoint on my local machine (they are on the same network). (replace baseUri with en0 ip)

When the CA DMV app scans the QR code, this entry shows up. But the app interface itself doesn't load.

mongosh mongodb://localhost:27017/opencred_localhost
opencred_localhost> db.Exchanges.find()
[
  {
    _id: ObjectId('66677f1fcd87358ea3cb7ffa'),
    id: 'z19nF6KMj97aQakPMpXvqXc9K',
    workflowId: 'z1A32xJZGqBeAEcMq56avmw2L',
    sequence: 0,
    ttl: 900,
    state: 'pending',
    variables: {},
    step: 'default',
    challenge: 'z1A55cGdqpyCC4S3VPXqPSL6B',
    accessToken: 'z1AF1Mc3LX66z1GFrvbber7HZ',
    createdAt: ISODate('2024-06-10T22:33:03.778Z'),
    recordExpiresAt: ISODate('2024-06-11T22:48:03.778Z'),
    oidc: { code: null, state: '' }
  }
]
deshmukhrajvardhan commented 3 weeks ago

I just realized that the Exchanges are created when this endpoint is called http://localhost:22080/context/login?client_id=rp1&redirect_uri=http%3A%2F%2Flocalhost%3A3000&scope=openid&response_type=code and not when CA DMV app scans it.

Upon scanning the code CA DMV app launches correctly, and I can see that it makes two distinct calls to my ngrok service

@tmarkovski how do you see these logs? On the server side, i assume?