mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-09 05:27:57 -05:00
* [INJIMOB-3392] add token request logic in wallet for vci flow Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] chore: update integration of VCIClient native module Changes are updated as per new changes in the library Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3390] refactor: event structure of token request Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3392] fix tokenEndpoint method and refactorings Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] cnonce decode from accesstoken and credential response destructuring fix Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3390] add: getIssuerMetadata in kotlin NativeModule Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3393] fix: auth callback in android Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3390] fix: proofJwt issue in download flow Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3392] fix credentialofferflow Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392]fix format issues in bridge layer Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392]fix activity log texts on application reopen Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392]cache issuer metadata by key: issuerhost Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] fix error scenarios and cleanup issuermachine Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] refactor request method to handle missing error scenarios Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] fix max lines for txcode description to 2 Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] rename credentialissueruri to credentialissuer Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] take cnonce from outside accesstoken Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] declare random-values at entry file Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] set fallback keytype to user priority first Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] add locales for network request failed error Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] remove console log Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-3392] refactor and clean up code Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> --------- Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> Co-authored-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>
308 lines
11 KiB
Swift
308 lines
11 KiB
Swift
import Foundation
|
|
import React
|
|
import VCIClient
|
|
|
|
@objc(InjiVciClient)
|
|
class RNVCIClientModule: NSObject, RCTBridgeModule {
|
|
|
|
private var vciClient: VCIClient?
|
|
|
|
private var pendingProofContinuation: ((String) -> Void)?
|
|
private var pendingAuthCodeContinuation: ((String) -> Void)?
|
|
private var pendingTxCodeContinuation: ((String) -> Void)?
|
|
private var pendingTokenResponseContinuation: ((String) -> Void)?
|
|
private var pendingIssuerTrustDecision: ((Bool) -> Void)?
|
|
|
|
static func moduleName() -> String {
|
|
return "InjiVciClient"
|
|
}
|
|
|
|
@objc
|
|
func `init`(_ traceabilityId: String) {
|
|
vciClient = VCIClient(traceabilityId: traceabilityId)
|
|
}
|
|
|
|
// MARK: - Public API
|
|
|
|
@objc
|
|
func requestCredentialByOffer(
|
|
_ credentialOffer: String,
|
|
clientMetadata: String,
|
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
) {
|
|
Task {
|
|
do {
|
|
guard let vciClient = vciClient else {
|
|
reject(nil, "VCIClient not initialized", nil)
|
|
return
|
|
}
|
|
|
|
let clientMeta = try parseClientMetadata(from: clientMetadata)
|
|
|
|
let response = try await vciClient.requestCredentialByCredentialOffer(
|
|
credentialOffer: credentialOffer,
|
|
clientMetadata: clientMeta,
|
|
getTxCode: { inputMode, description, length in
|
|
try await self.getTxCodeHook(
|
|
inputMode: inputMode,
|
|
description: description,
|
|
length: length
|
|
)
|
|
},
|
|
authorizeUser: { authUrl in
|
|
try await self.getAuthCodeContinuationHook(authUrl: authUrl)
|
|
},
|
|
getTokenResponse: { tokenRequest in
|
|
try await self.getTokenResponseHook(tokenRequest: tokenRequest)
|
|
},
|
|
getProofJwt: { credentialIssuer, cNonce, algos in
|
|
try await self.getProofContinuationHook(
|
|
credentialIssuer: credentialIssuer,
|
|
cNonce: cNonce,
|
|
proofSigningAlgorithmsSupported: algos
|
|
)
|
|
},
|
|
onCheckIssuerTrust: { credentialIssuer, issuerDisplay in
|
|
try await self.getIssuerTrustDecisionHook(
|
|
credentialIssuer: credentialIssuer,
|
|
issuerDisplay: issuerDisplay
|
|
)
|
|
}
|
|
)
|
|
|
|
resolve(try response?.toJsonString())
|
|
} catch {
|
|
reject(nil, error.localizedDescription, nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc
|
|
func requestCredentialFromTrustedIssuer(
|
|
_ credentialIssuer: String,
|
|
credentialConfigurationId: String,
|
|
clientMetadata: String,
|
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
) {
|
|
Task {
|
|
do {
|
|
guard let vciClient = vciClient else {
|
|
reject(nil, "VCIClient not initialized", nil)
|
|
return
|
|
}
|
|
|
|
let clientMeta = try parseClientMetadata(from: clientMetadata)
|
|
|
|
let response = try await vciClient.requestCredentialFromTrustedIssuer(
|
|
credentialIssuer: credentialIssuer,
|
|
credentialConfigurationId: credentialConfigurationId,
|
|
clientMetadata: clientMeta,
|
|
authorizeUser: { authUrl in
|
|
try await self.getAuthCodeContinuationHook(authUrl: authUrl)
|
|
},
|
|
getTokenResponse: { tokenRequest in
|
|
try await self.getTokenResponseHook(tokenRequest: tokenRequest)
|
|
},
|
|
getProofJwt: { credentialIssuer, cNonce, algos in
|
|
try await self.getProofContinuationHook(
|
|
credentialIssuer: credentialIssuer,
|
|
cNonce: cNonce,
|
|
proofSigningAlgorithmsSupported: algos
|
|
)
|
|
}
|
|
)
|
|
|
|
resolve(try response?.toJsonString())
|
|
} catch {
|
|
reject(nil, error.localizedDescription, nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc
|
|
func getIssuerMetadata(
|
|
_ credentialIssuer: String,
|
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
) {
|
|
Task {
|
|
do {
|
|
guard let vciClient = vciClient else {
|
|
reject(nil, "VCIClient not initialized", nil)
|
|
return
|
|
}
|
|
|
|
let metadata = try await vciClient.getIssuerMetadata(credentialIssuer: credentialIssuer)
|
|
|
|
let data = try JSONSerialization.data(withJSONObject: metadata, options: [])
|
|
guard let jsonString = String(data: data, encoding: .utf8) else {
|
|
throw NSError(domain: "JSONEncodingError", code: 0)
|
|
}
|
|
|
|
resolve(jsonString)
|
|
} catch {
|
|
reject(nil, error.localizedDescription, nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - Callbacks to JS
|
|
|
|
private func getTxCodeHook(
|
|
inputMode: String?,
|
|
description: String?,
|
|
length: Int?
|
|
) async throws -> String {
|
|
if let bridge = RCTBridge.current() {
|
|
bridge.eventDispatcher().sendAppEvent(
|
|
withName: "onRequestTxCode",
|
|
body: [
|
|
"inputMode": inputMode,
|
|
"description": description,
|
|
"length": length
|
|
]
|
|
)
|
|
}
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
self.pendingTxCodeContinuation = { code in continuation.resume(returning: code) }
|
|
}
|
|
}
|
|
|
|
private func getAuthCodeContinuationHook(authUrl: String) async throws -> String {
|
|
if let bridge = RCTBridge.current() {
|
|
bridge.eventDispatcher().sendAppEvent(
|
|
withName: "onRequestAuthCode",
|
|
body: ["authorizationUrl": authUrl]
|
|
)
|
|
}
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
self.pendingAuthCodeContinuation = { code in continuation.resume(returning: code) }
|
|
}
|
|
}
|
|
|
|
private func getProofContinuationHook(
|
|
credentialIssuer: String,
|
|
cNonce: String?,
|
|
proofSigningAlgorithmsSupported: [String]
|
|
) async throws -> String {
|
|
let jsonData = try JSONSerialization.data(withJSONObject: proofSigningAlgorithmsSupported, options: [])
|
|
let jsonString = String(data: jsonData, encoding: .utf8) ?? "{}"
|
|
if let bridge = RCTBridge.current() {
|
|
bridge.eventDispatcher().sendAppEvent(
|
|
withName: "onRequestProof",
|
|
body: [
|
|
"credentialIssuer": credentialIssuer,
|
|
"cNonce": cNonce,
|
|
"proofSigningAlgorithmsSupported": jsonString
|
|
]
|
|
)
|
|
}
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
self.pendingProofContinuation = { jwt in continuation.resume(returning: jwt) }
|
|
}
|
|
}
|
|
|
|
private func getTokenResponseHook(tokenRequest: TokenRequest) async throws -> TokenResponse {
|
|
if let bridge = RCTBridge.current() {
|
|
let tokenRequest: [String: Any] = [
|
|
"grantType": tokenRequest.grantType.rawValue,
|
|
"tokenEndpoint": tokenRequest.tokenEndpoint,
|
|
"authCode": tokenRequest.authCode ?? NSNull(),
|
|
"preAuthCode": tokenRequest.preAuthCode ?? NSNull(),
|
|
"txCode": tokenRequest.txCode ?? NSNull(),
|
|
"clientId": tokenRequest.clientId ?? NSNull(),
|
|
"redirectUri": tokenRequest.redirectUri ?? NSNull(),
|
|
"codeVerifier": tokenRequest.codeVerifier ?? NSNull()
|
|
]
|
|
|
|
bridge.eventDispatcher().sendAppEvent(
|
|
withName: "onRequestTokenResponse",
|
|
body: ["tokenRequest":tokenRequest]
|
|
)
|
|
}
|
|
|
|
let json = try await withCheckedThrowingContinuation { continuation in
|
|
self.pendingTokenResponseContinuation = { json in continuation.resume(returning: json) }
|
|
}
|
|
|
|
guard let data = json.data(using: .utf8) else {
|
|
throw NSError(domain: "Invalid JSON", code: 0)
|
|
}
|
|
|
|
return try JSONDecoder().decode(TokenResponse.self, from: data)
|
|
}
|
|
|
|
private func getIssuerTrustDecisionHook(
|
|
credentialIssuer: String,
|
|
issuerDisplay: [[String: Any]]
|
|
) async throws -> Bool {
|
|
// Convert issuerDisplay to JSON string
|
|
let jsonData = try JSONSerialization.data(withJSONObject: issuerDisplay, options: [])
|
|
let jsonString = String(data: jsonData, encoding: .utf8) ?? "[]"
|
|
if let bridge = RCTBridge.current() {
|
|
bridge.eventDispatcher().sendAppEvent(
|
|
withName: "onCheckIssuerTrust",
|
|
body: [
|
|
"credentialIssuer": credentialIssuer,
|
|
"issuerDisplay": jsonString
|
|
]
|
|
)
|
|
}
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
self.pendingIssuerTrustDecision = { decision in continuation.resume(returning: decision) }
|
|
}
|
|
}
|
|
|
|
// MARK: - Receivers from JS
|
|
|
|
@objc(sendProofFromJS:)
|
|
func sendProofFromJS(_ jwt: String) {
|
|
pendingProofContinuation?(jwt)
|
|
pendingProofContinuation = nil
|
|
}
|
|
|
|
@objc(sendAuthCodeFromJS:)
|
|
func sendAuthCodeFromJS(_ code: String) {
|
|
pendingAuthCodeContinuation?(code)
|
|
pendingAuthCodeContinuation = nil
|
|
}
|
|
|
|
@objc(sendTxCodeFromJS:)
|
|
func sendTxCodeFromJS(_ code: String) {
|
|
pendingTxCodeContinuation?(code)
|
|
pendingTxCodeContinuation = nil
|
|
}
|
|
|
|
@objc(sendTokenResponseFromJS:)
|
|
func sendTokenResponseFromJS(_ json: String) {
|
|
pendingTokenResponseContinuation?(json)
|
|
pendingTokenResponseContinuation = nil
|
|
}
|
|
|
|
@objc(sendIssuerTrustResponseFromJS:)
|
|
func sendIssuerTrustResponseFromJS(_ trusted: Bool) {
|
|
pendingIssuerTrustDecision?(trusted)
|
|
pendingIssuerTrustDecision = nil
|
|
}
|
|
|
|
// MARK: - JSON Parsing
|
|
|
|
private func parseClientMetadata(from jsonString: String) throws -> ClientMetadata {
|
|
guard let data = jsonString.data(using: .utf8) else {
|
|
throw NSError(domain: "Invalid JSON string for clientMetadata", code: 0)
|
|
}
|
|
return try JSONDecoder().decode(ClientMetadata.self, from: data)
|
|
}
|
|
|
|
@objc static func requiresMainQueueSetup() -> Bool {
|
|
return true
|
|
}
|
|
}
|