@@ -26,64 +26,64 @@ struct HandleOID4VCIView: View {
2626
2727 func getCredential( credentialOffer: String ) {
2828 loading = true
29- let client = Oid4vciAsyncHttpClient ( )
30- let oid4vciSession = Oid4vci . newWithAsyncClient ( client: client)
29+
30+ // Setup HTTP client.
31+ let httpClient = Oid4vciAsyncHttpClient ( )
32+
33+ // Setup signer.
34+ let jwk = KeyManager . getOrInsertJwk ( id: DEFAULT_SIGNING_KEY_ID)
35+ let didUrl = generateDidJwkUrl ( jwk: jwk)
36+ jwk. setKid ( kid: didUrl. description)
37+ let signer = KeyManagerJwkSigner ( id: DEFAULT_SIGNING_KEY_ID, jwk: jwk)
38+
39+ let clientId = didUrl. did ( ) . description
40+ let oid4vciClient = Oid4vciClient ( clientId: clientId)
41+
3142 Task {
3243 do {
33- try await oid4vciSession. initiateWithOffer (
34- credentialOffer: credentialOffer,
35- clientId: " skit-demo-wallet " ,
36- redirectUrl: " https://spruceid.com "
37- )
38-
39- let nonce = try await oid4vciSession. exchangeToken ( )
40-
41- let metadata = try oid4vciSession. getMetadata ( )
42-
43- if !KeyManager. keyExists ( id: DEFAULT_SIGNING_KEY_ID) {
44- _ = KeyManager . generateSigningKey (
45- id: DEFAULT_SIGNING_KEY_ID
46- )
44+ let offerUrl = if url. starts ( with: " openid-credential-offer:// " ) {
45+ url
46+ } else {
47+ " openid-credential-offer:// \( url) "
4748 }
48-
49- let jwk = KeyManager . getJwk ( id: DEFAULT_SIGNING_KEY_ID)
50-
51- let signingInput =
52- try await SpruceIDMobileSdkRs . generatePopPrepare (
53- audience: metadata. issuer ( ) ,
54- nonce: nonce,
55- didMethod: . jwk,
56- publicJwk: jwk!,
57- durationInSecs: nil
58- )
59-
60- let signature = KeyManager . signPayload (
61- id: DEFAULT_SIGNING_KEY_ID,
62- payload: [ UInt8] ( signingInput)
63- )
64-
65- let pop = try SpruceIDMobileSdkRs . generatePopComplete (
66- signingInput: signingInput,
67- signatureDer: Data ( signature!)
68- )
69-
70- try oid4vciSession. setContextMap (
71- values: getVCPlaygroundOID4VCIContext ( )
72- )
73-
74- self . credentialPack = CredentialPack ( )
75- let credentials = try await oid4vciSession. exchangeCredential (
76- proofsOfPossession: [ pop] ,
77- options: Oid4vciExchangeOptions ( verifyAfterExchange: false )
78- )
79-
80- credentials. forEach {
81- let cred = String ( decoding: Data ( $0. payload) , as: UTF8 . self)
82- self . credential = cred
49+
50+ let credentialOffer = try await oid4vciClient. resolveOfferUrl ( httpClient: httpClient, credentialOfferUrl: offerUrl)
51+ let credentialIssuer = credentialOffer. credentialIssuer ( )
52+
53+ let state = try await oid4vciClient. acceptOffer ( httpClient: httpClient, credentialOffer: credentialOffer)
54+
55+ switch state {
56+ case . requiresAuthorizationCode( _) :
57+ err = " Authorization Code Grant not supported "
58+ case . requiresTxCode( _) :
59+ err = " Transaction Code not supported "
60+ case . ready( let credentialToken) :
61+ let credentialId = try credentialToken. defaultCredentialId ( )
62+
63+ // Generate Proof of Possession.
64+ let nonce = try await credentialToken. getNonce ( httpClient: httpClient)
65+ let jwt = try await createJwtProof ( issuer: clientId, audience: credentialIssuer, expireInSecs: nil , nonce: nonce, signer: signer)
66+ let proofs = Proofs . jwt ( [ jwt] )
67+
68+ // Exchange token against credential.
69+ let response = try await oid4vciClient. exchangeCredential ( httpClient: httpClient, token: credentialToken, credential: credentialId, proofs: proofs)
70+
71+ switch response {
72+ case . deferred( _) :
73+ err = " Deferred credentials not supported "
74+ case . immediate( let response) :
75+ guard let rawCredential = response. credentials. first else {
76+ throw NSError ( domain: " OID4VCI " , code: 0 , userInfo: [
77+ " CredentialOfferUrl " : offerUrl,
78+ " CredentialIssuer " : credentialIssuer
79+ ] )
80+ }
81+
82+ credential = String ( decoding: Data ( rawCredential. payload) , as: UTF8 . self)
83+
84+ onSuccess ? ( )
85+ }
8386 }
84-
85- onSuccess ? ( )
86-
8787 } catch {
8888 err = error. localizedDescription
8989 print ( error)
@@ -119,6 +119,27 @@ struct HandleOID4VCIView: View {
119119 }
120120}
121121
122+ class KeyManagerJwkSigner : JwsSigner , @unchecked Sendable {
123+ let id : String
124+ let jwk : Jwk
125+
126+ init ( id: String , jwk: Jwk ) {
127+ self . id = id
128+ self . jwk = jwk
129+ }
130+
131+ func fetchInfo( ) async throws -> JwsSignerInfo {
132+ return try await jwk. fetchInfo ( )
133+ }
134+
135+ func signBytes( signingBytes: Data ) async throws -> Data {
136+ return try decodeDerSignature ( signatureDer: Data ( KeyManager . signPayload (
137+ id: DEFAULT_SIGNING_KEY_ID,
138+ payload: [ UInt8] ( signingBytes)
139+ ) !) )
140+ }
141+ }
142+
122143func getVCPlaygroundOID4VCIContext( ) throws -> [ String : String ] {
123144 var context : [ String : String ] = [ : ]
124145
0 commit comments