mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-08 21:18:14 -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>
383 lines
12 KiB
TypeScript
383 lines
12 KiB
TypeScript
import NetInfo from '@react-native-community/netinfo';
|
|
import { NativeModules } from 'react-native';
|
|
import Cloud from '../../shared/CloudBackupAndRestoreUtils';
|
|
import getAllConfigurations, { CACHED_API } from '../../shared/api';
|
|
import {
|
|
fetchKeyPair,
|
|
generateKeyPair,
|
|
} from '../../shared/cryptoutil/cryptoUtil';
|
|
import {
|
|
constructProofJWT,
|
|
hasKeyPair,
|
|
updateCredentialInformation,
|
|
verifyCredentialData,
|
|
} from '../../shared/openId4VCI/Utils';
|
|
import VciClient from '../../shared/vciClient/VciClient';
|
|
import { displayType, issuerType } from './IssuersMachine';
|
|
import { setItem } from '../store';
|
|
import { API_CACHED_STORAGE_KEYS } from '../../shared/constants';
|
|
import { createCacheObject } from '../../shared/Utils';
|
|
import { VerificationResult } from '../../shared/vcjs/verifyCredential';
|
|
|
|
export const IssuersService = () => {
|
|
return {
|
|
isUserSignedAlready: () => async () => {
|
|
return await Cloud.isSignedInAlready();
|
|
},
|
|
downloadIssuersList: async () => {
|
|
const trustedIssuersList = await CACHED_API.fetchIssuers();
|
|
return trustedIssuersList;
|
|
},
|
|
checkInternet: async () => await NetInfo.fetch(),
|
|
downloadIssuerWellknown: async (context: any) => {
|
|
const wellknownResponse = (await VciClient.getInstance().getIssuerMetadata(
|
|
context.selectedIssuer.credential_issuer_host,
|
|
)) as issuerType;
|
|
const wellknownCacheObject = createCacheObject(wellknownResponse);
|
|
await setItem(
|
|
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(
|
|
context.selectedIssuer.credential_issuer_host,
|
|
),
|
|
wellknownCacheObject,
|
|
'',
|
|
);
|
|
return wellknownResponse;
|
|
},
|
|
getCredentialTypes: async (context: any) => {
|
|
const credentialTypes: Array<{id: string; [key: string]: any}> = [];
|
|
const selectedIssuer = context.selectedIssuer;
|
|
|
|
const keys = Object.keys(
|
|
selectedIssuer.credential_configurations_supported,
|
|
);
|
|
|
|
for (const key of keys) {
|
|
if (selectedIssuer.credential_configurations_supported[key]) {
|
|
credentialTypes.push({
|
|
id: key,
|
|
...selectedIssuer.credential_configurations_supported[key],
|
|
});
|
|
}
|
|
}
|
|
|
|
if (credentialTypes.length === 0) {
|
|
throw new Error(
|
|
`No credential type found for issuer ${selectedIssuer.issuer_id}`,
|
|
);
|
|
}
|
|
return credentialTypes;
|
|
},
|
|
|
|
downloadCredential: (context: any) => async (sendBack: any) => {
|
|
const navigateToAuthView = (authorizationEndpoint: string) => {
|
|
sendBack({
|
|
type: 'AUTH_ENDPOINT_RECEIVED',
|
|
authEndpoint: authorizationEndpoint,
|
|
});
|
|
};
|
|
const getProofJwt = async (
|
|
credentialIssuer: string,
|
|
cNonce: string | null,
|
|
proofSigningAlgosSupported: string[] | null,
|
|
) => {
|
|
sendBack({
|
|
type: 'PROOF_REQUEST',
|
|
credentialIssuer: credentialIssuer,
|
|
cNonce: cNonce,
|
|
proofSigningAlgosSupported: proofSigningAlgosSupported,
|
|
});
|
|
};
|
|
const getTokenResponse = (tokenRequest: object) => {
|
|
sendBack({
|
|
type: 'TOKEN_REQUEST',
|
|
tokenRequest: tokenRequest,
|
|
});
|
|
};
|
|
const {credential} =
|
|
await VciClient.getInstance().requestCredentialFromTrustedIssuer(
|
|
context.selectedIssuer.credential_issuer_host,
|
|
context.selectedCredentialType.id,
|
|
{
|
|
clientId: context.selectedIssuer.client_id,
|
|
redirectUri: context.selectedIssuer.redirect_uri,
|
|
},
|
|
getProofJwt,
|
|
navigateToAuthView,
|
|
getTokenResponse,
|
|
);
|
|
return updateCredentialInformation(context, credential);
|
|
},
|
|
sendTxCode: async (context: any) => {
|
|
await VciClient.getInstance().sendTxCode(context.txCode);
|
|
},
|
|
|
|
sendConsentGiven: async () => {
|
|
await VciClient.getInstance().sendIssuerConsent(true);
|
|
},
|
|
|
|
sendConsentNotGiven: async () => {
|
|
await VciClient.getInstance().sendIssuerConsent(false);
|
|
},
|
|
|
|
checkIssuerIdInStoredTrustedIssuers: async (context: any) => {
|
|
const {RNSecureKeystoreModule} = NativeModules;
|
|
try {
|
|
return await RNSecureKeystoreModule.hasAlias(
|
|
context.credentialOfferCredentialIssuer,
|
|
);
|
|
} catch (error) {
|
|
console.error(
|
|
`Error while checking issuer ID in trusted issuers:`,
|
|
error,
|
|
);
|
|
return false;
|
|
}
|
|
},
|
|
addIssuerToTrustedIssuers: async (context: any) => {
|
|
const {RNSecureKeystoreModule} = NativeModules;
|
|
try {
|
|
await RNSecureKeystoreModule.storeData(
|
|
context.credentialOfferCredentialIssuer,
|
|
'trusted',
|
|
);
|
|
} catch {
|
|
console.error('Error updating issuer trust in keystore');
|
|
}
|
|
},
|
|
downloadCredentialFromOffer: (context: any) => async (sendBack: any) => {
|
|
const navigateToAuthView = (authorizationEndpoint: string) => {
|
|
sendBack({
|
|
type: 'AUTH_ENDPOINT_RECEIVED',
|
|
authEndpoint: authorizationEndpoint,
|
|
});
|
|
};
|
|
const getSignedProofJwt = async (
|
|
credentialIssuer: string,
|
|
cNonce: string | null,
|
|
proofSigningAlgosSupported: string[] | null,
|
|
) => {
|
|
sendBack({
|
|
type: 'PROOF_REQUEST',
|
|
cNonce: cNonce,
|
|
issuer: credentialIssuer,
|
|
proofSigningAlgosSupported: proofSigningAlgosSupported,
|
|
});
|
|
};
|
|
|
|
const getTxCode = async (
|
|
inputMode: string | undefined,
|
|
description: string | undefined,
|
|
length: number | undefined,
|
|
) => {
|
|
sendBack({
|
|
type: 'TX_CODE_REQUEST',
|
|
inputMode: inputMode,
|
|
description: description,
|
|
length: length,
|
|
});
|
|
};
|
|
|
|
const requesTrustIssuerConsent = async (
|
|
credentialIssuer: string,
|
|
issuerDisplay: object[],
|
|
) => {
|
|
const issuerDisplayObject = issuerDisplay as displayType[];
|
|
|
|
sendBack({
|
|
type: 'TRUST_ISSUER_CONSENT_REQUEST',
|
|
issuerDisplay: issuerDisplayObject,
|
|
issuer: credentialIssuer,
|
|
});
|
|
};
|
|
const getTokenResponse = (tokenRequest: object) => {
|
|
sendBack({
|
|
type: 'TOKEN_REQUEST',
|
|
tokenRequest: tokenRequest,
|
|
});
|
|
};
|
|
|
|
const credentialResponse =
|
|
await VciClient.getInstance().requestCredentialByOffer(
|
|
context.qrData,
|
|
getTxCode,
|
|
getSignedProofJwt,
|
|
navigateToAuthView,
|
|
getTokenResponse,
|
|
requesTrustIssuerConsent,
|
|
);
|
|
return credentialResponse;
|
|
},
|
|
sendTokenRequest: async (context: any) => {
|
|
const tokenRequestObject = context.tokenRequestObject;
|
|
return await sendTokenRequest(
|
|
tokenRequestObject,
|
|
context.selectedIssuer?.token_endpoint,
|
|
);
|
|
},
|
|
sendTokenResponse: async (context: any) => {
|
|
const tokenResponse = context.tokenResponse;
|
|
if (!tokenResponse) {
|
|
throw new Error(
|
|
'Could not send token response, tokenResponse is undefined or null',
|
|
);
|
|
}
|
|
return await VciClient.getInstance().sendTokenResponse(
|
|
JSON.stringify(tokenResponse),
|
|
);
|
|
},
|
|
|
|
updateCredential: async (context: any) => {
|
|
const credential = await updateCredentialInformation(
|
|
context,
|
|
context.credential,
|
|
);
|
|
return credential;
|
|
},
|
|
cacheIssuerWellknown: async (context: any) => {
|
|
const credentialIssuer = context.credentialOfferCredentialIssuer;
|
|
const issuerMetadata = (await VciClient.getInstance().getIssuerMetadata(
|
|
credentialIssuer,
|
|
)) as issuerType;
|
|
const wellknownCacheObject = createCacheObject(issuerMetadata);
|
|
await setItem(
|
|
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(credentialIssuer),
|
|
wellknownCacheObject,
|
|
'',
|
|
);
|
|
return issuerMetadata;
|
|
},
|
|
constructProof: async (context: any) => {
|
|
const proofJWT = await constructProofJWT(
|
|
context.publicKey,
|
|
context.privateKey,
|
|
context.credentialOfferCredentialIssuer,
|
|
null,
|
|
context.keyType,
|
|
context.wellknownKeyTypes,
|
|
true,
|
|
context.cNonce,
|
|
);
|
|
await VciClient.getInstance().sendProof(proofJWT);
|
|
return proofJWT;
|
|
},
|
|
constructAndSendProofForTrustedIssuers: async (context: any) => {
|
|
const issuerMeta = context.selectedIssuer;
|
|
const proofJWT = await constructProofJWT(
|
|
context.publicKey,
|
|
context.privateKey,
|
|
context.selectedIssuer.credential_issuer_host,
|
|
context.selectedIssuer.client_id,
|
|
context.keyType,
|
|
context.wellknownKeyTypes,
|
|
false,
|
|
context.cNonce,
|
|
);
|
|
await VciClient.getInstance().sendProof(proofJWT);
|
|
return proofJWT;
|
|
},
|
|
|
|
getKeyOrderList: async () => {
|
|
const {RNSecureKeystoreModule} = NativeModules;
|
|
const keyOrder = JSON.parse(
|
|
(await RNSecureKeystoreModule.getData('keyPreference'))[1],
|
|
);
|
|
return keyOrder;
|
|
},
|
|
|
|
generateKeyPair: async (context: any) => {
|
|
const keypair = await generateKeyPair(context.keyType);
|
|
return keypair;
|
|
},
|
|
|
|
getKeyPair: async (context: any) => {
|
|
if (context.keyType === '') {
|
|
throw new Error('key type not found');
|
|
} else if (!!(await hasKeyPair(context.keyType))) {
|
|
return await fetchKeyPair(context.keyType);
|
|
}
|
|
},
|
|
|
|
getSelectedKey: async (context: any) => {
|
|
return context.keyType;
|
|
},
|
|
|
|
verifyCredential: async (context: any): Promise<VerificationResult> => {
|
|
const { isCredentialOfferFlow, verifiableCredential, selectedCredentialType } = context;
|
|
if (isCredentialOfferFlow) {
|
|
const configurations = await getAllConfigurations();
|
|
if (configurations.disableCredentialOfferVcVerification) {
|
|
return {
|
|
isVerified: true,
|
|
verificationMessage: '',
|
|
verificationErrorCode: '',
|
|
};
|
|
}
|
|
}
|
|
const verificationResult = await verifyCredentialData(
|
|
verifiableCredential?.credential,
|
|
selectedCredentialType.format,
|
|
);
|
|
if (!verificationResult.isVerified) {
|
|
throw new Error(verificationResult.verificationErrorCode);
|
|
}
|
|
|
|
return verificationResult;
|
|
}
|
|
|
|
}
|
|
}
|
|
async function sendTokenRequest(
|
|
tokenRequestObject: any,
|
|
proxyTokenEndpoint: any = null,
|
|
) {
|
|
if (proxyTokenEndpoint) {
|
|
tokenRequestObject.tokenEndpoint = proxyTokenEndpoint;
|
|
}
|
|
if (!tokenRequestObject?.tokenEndpoint) {
|
|
console.error('tokenEndpoint is not provided in tokenRequestObject');
|
|
throw new Error('tokenEndpoint is required');
|
|
}
|
|
|
|
const formBody = new URLSearchParams();
|
|
|
|
formBody.append('grant_type', tokenRequestObject.grantType);
|
|
|
|
if (tokenRequestObject.authCode) {
|
|
formBody.append('code', tokenRequestObject.authCode);
|
|
}
|
|
if (tokenRequestObject.preAuthCode) {
|
|
formBody.append('pre-authorized_code', tokenRequestObject.preAuthCode);
|
|
}
|
|
if (tokenRequestObject.txCode) {
|
|
formBody.append('tx_code', tokenRequestObject.txCode);
|
|
}
|
|
if (tokenRequestObject.clientId) {
|
|
formBody.append('client_id', tokenRequestObject.clientId);
|
|
}
|
|
if (tokenRequestObject.redirectUri) {
|
|
formBody.append('redirect_uri', tokenRequestObject.redirectUri);
|
|
}
|
|
if (tokenRequestObject.codeVerifier) {
|
|
formBody.append('code_verifier', tokenRequestObject.codeVerifier);
|
|
}
|
|
const response = await fetch(tokenRequestObject.tokenEndpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
},
|
|
body: formBody.toString(),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error(
|
|
'Token request failed with status:',
|
|
response.status,
|
|
errorText,
|
|
);
|
|
throw new Error(`Token request failed: ${response.status} ${errorText}`);
|
|
}
|
|
const tokenResponse = await response.json();
|
|
return tokenResponse;
|
|
}
|