Skip to content

Commit

Permalink
feat: DeigiLocker intigration
Browse files Browse the repository at this point in the history
Signed-off-by: tusharbhayani <tushar.bhayani@ayanworks.com>
  • Loading branch information
tusharbhayani committed Oct 30, 2024
1 parent 5c785e2 commit c222724
Show file tree
Hide file tree
Showing 5 changed files with 786 additions and 457 deletions.
14 changes: 13 additions & 1 deletion packages/digilocker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
"files": [
"build"
],
"dependencies": {
"react-native": "0.72.5",
"axios": "^1.6.0",
"crypto-js": "^4.2.0",
"xml2js": "^0.6.2",
"uuid": "^9.0.0"
},
"scripts": {
"check-types": "pnpm compile --noEmit",
"build": "pnpm clean && pnpm compile",
Expand All @@ -26,6 +33,11 @@
"devDependencies": {
"@types/node": "^18.18.8",
"rimraf": "3.0.2",
"typescript": "~5.5.2"
"typescript": "~5.5.2",
"@types/xml2js": "~0.4.14",
"@types/uuid": "^9.0.2"
},
"peerDependencies": {
"react-native": "0.72.5"
}
}
127 changes: 127 additions & 0 deletions packages/digilocker/src/digilocker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import axios from 'axios'
import { createHash } from 'crypto'

export type AdeyaDigiLockerModuleOptions = {
client_id?: string | undefined
client_secret?: string | undefined
redirect_url?: string | undefined
authCode?: string | undefined
codeVerifier?: string | undefined
}

export const base64UrlEncodeWithoutPadding = (input: Buffer): string => {
return input.toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
}

export const generateCodeChallenge = (codeVerifier: string): string => {
const hash = createHash('sha256').update(codeVerifier).digest()
return base64UrlEncodeWithoutPadding(hash)
}

export const initiateDigiLockerOAuth = async ({
client_id = '',
redirect_url = '',
codeVerifier = ''
}: AdeyaDigiLockerModuleOptions) => {
try {
const codeChallenge = generateCodeChallenge(codeVerifier)
const authUrl = `https://api.digitallocker.gov.in/public/oauth2/1/authorize?response_type=code&client_id=${client_id}&redirect_uri=${redirect_url}&state=adeya2024&code_challenge=${codeChallenge}&code_challenge_method=S256`
return authUrl
} catch (error) {
return error instanceof Error ? error : new Error('An unknown error occurred')
}
}

export const fetchDigiLockerToken = async ({
authCode = '',
client_id = '',
client_secret = '',
redirect_url = '',
codeVerifier = ''
}: AdeyaDigiLockerModuleOptions) => {
const tokenUrl = 'https://api.digitallocker.gov.in/public/oauth2/1/token'

const params =
`grant_type=authorization_code&` +
`code=${encodeURIComponent(authCode)}&` +
`client_id=${encodeURIComponent(client_id)}&` +
`client_secret=${encodeURIComponent(client_secret)}&` +
`redirect_uri=${encodeURIComponent(redirect_url)}&` +
`code_verifier=${encodeURIComponent(codeVerifier)}`

try {
const response = await axios.post(tokenUrl, params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
return response.data
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'
return { message: `Error fetching DigiLocker token: ${errorMessage}` }
}
}

export const fetchAadhaarData = async (accessToken: string): Promise<{ message: string }> => {
const aadhaarUrl = 'https://api.digitallocker.gov.in/public/oauth2/3/xml/eaadhaar'

try {
const response = await axios.get(aadhaarUrl, {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
return response.data
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'
return { message: `Error fetching Aadhaar data: ${errorMessage}` }
}
}

export const fetchIssuedDocuments = async (accessToken: string): Promise<{ message: string }> => {
const issuedDocumentsUrl = 'https://api.digitallocker.gov.in/public/oauth2/2/files/issued'

try {
const response = await axios.get(issuedDocumentsUrl, {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
return response.data
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'
return { message: `Error fetching issued documents: ${errorMessage}` }
}
}

export const fetchDocumentData = async (uri: string, accessToken: string): Promise<{ message: string }> => {
const documentUrl = `https://api.digitallocker.gov.in/public/oauth2/1/xml/${uri}`

try {
const response = await axios.get(documentUrl, {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
return response.data
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'
return { message: `Error fetching document data: ${errorMessage}` }
}
}

export const fetchDocument = async (uri: string, accessToken: string): Promise<{ message: string }> => {
const documentUrl = `https://api.digitallocker.gov.in/public/oauth2/1/file/${uri}`

try {
const response = await axios.get(documentUrl, {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
return response.data
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'
return { message: `Error fetching document data: ${errorMessage}` }
}
}
166 changes: 166 additions & 0 deletions packages/digilocker/src/digilockerDataParse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { parseStringPromise } from 'xml2js'

interface AadhaarData {
uid: string
dob: string
gender: string
name: string
co: string
country: string
district: string
locality: string
pincode: string
state: string
vtc: string
house: string
street: string
landmark: string
postOffice: string
photo: string
}

interface PANData {
panNumber: string
name: string
dob: string
gender: string
}

interface DrivingLicenseData {
licenseNumber: string
issuedAt: string
issueDate: string
expiryDate: string
dob: string
swd: string
swdIndicator: string
gender: string
presentAddressLine1: string
presentAddressLine2: string
presentAddressHouse: string
presentAddressLandmark: string
presentAddressLocality: string
presentAddressVtc: string
presentAddressDistrict: string
presentAddressPin: string
presentAddressState: string
presentAddressCountry: string
permanentAddressLine1: string
permanentAddressLine2: string
permanentAddressHouse: string
permanentAddressLandmark: string
permanentAddressLocality: string
permanentAddressVtc: string
permanentAddressDistrict: string
permanentAddressPin: string
permanentAddressState: string
permanentAddressCountry: string
licenseTypes: string
name: string
photo: string
}

const getOrDefault = (obj: any, path: string[], defaultValue = ''): string => {
return path.reduce((acc, key) => (acc && acc[key] ? acc[key] : defaultValue), obj)
}

export const parseAadhaarData = async (xmlString: string): Promise<AadhaarData | { error: string }> => {
try {
const result = await parseStringPromise(xmlString)
const uidData = result.Certificate.CertificateData[0].KycRes[0].UidData[0]
const poi = uidData.Poi[0]
const poa = uidData.Poa[0]
const photo = uidData.Pht[0] || ''

const aadhaarData: AadhaarData = {
uid: getOrDefault(uidData, ['$', 'uid']),
dob: getOrDefault(poi, ['$', 'dob']),
gender: getOrDefault(poi, ['$', 'gender']),
name: getOrDefault(poi, ['$', 'name']),
co: getOrDefault(poa, ['$', 'co']),
country: getOrDefault(poa, ['$', 'country']),
district: getOrDefault(poa, ['$', 'dist']),
locality: getOrDefault(poa, ['$', 'loc']),
pincode: getOrDefault(poa, ['$', 'pc']),
state: getOrDefault(poa, ['$', 'state']),
vtc: getOrDefault(poa, ['$', 'vtc']),
house: getOrDefault(poa, ['$', 'house']),
street: getOrDefault(poa, ['$', 'street']),
landmark: getOrDefault(poa, ['$', 'lm']),
postOffice: getOrDefault(poa, ['$', 'po']),
photo
}
return aadhaarData
} catch (error) {
return { error: 'Error parsing Aadhaar XML. Please check the input data.' }
}
}

export const parsePANData = async (xmlString: string): Promise<PANData | { error: string }> => {
try {
const result = await parseStringPromise(xmlString)

const certificate = result?.Certificate
const issuedTo = certificate?.IssuedTo?.[0]?.Person?.[0]

const panData: PANData = {
panNumber: certificate?.$?.number || '',
name: issuedTo?.$?.name || '',
dob: issuedTo?.$?.dob || '',
gender: issuedTo?.$?.gender || ''
}

return panData
} catch (error) {
return { error: 'Error parsing PAN XML. Please check the input data.' }
}
}

export const parseDrivingLicenseData = async (xmlString: string): Promise<DrivingLicenseData | { error: string }> => {
try {
const result = await parseStringPromise(xmlString)

const certificate = result?.Certificate
const issuedTo = certificate?.IssuedTo?.[0]?.Person?.[0]
const presentAddress = issuedTo?.Address?.[0]?.$ || {}
const permanentAddress = issuedTo?.Address2?.[0]?.$ || {}
const licenseTypes = certificate?.CertificateData[0]?.DrivingLicense[0]?.Categories[0]?.Category

const drivingLicenseData: DrivingLicenseData = {
licenseNumber: certificate?.$?.number || '',
issuedAt: certificate?.$?.issuedAt || '',
issueDate: certificate?.$?.issueDate || '',
expiryDate: certificate?.$?.expiryDate || '',
dob: issuedTo?.$?.dob || '',
swd: issuedTo?.$?.swd || '',
swdIndicator: issuedTo?.$?.swdIndicator || '',
name: issuedTo?.$?.name || '',
presentAddressLine1: presentAddress.line1 || '',
presentAddressLine2: presentAddress.line2 || '',
presentAddressHouse: presentAddress.house || '',
presentAddressLandmark: presentAddress.landmark || '',
presentAddressLocality: presentAddress.locality || '',
presentAddressVtc: presentAddress.vtc || '',
presentAddressDistrict: presentAddress.district || '',
presentAddressPin: presentAddress.pin || '',
presentAddressState: presentAddress.state || '',
presentAddressCountry: presentAddress.country || '',
permanentAddressLine1: permanentAddress.line1 || '',
permanentAddressLine2: permanentAddress.line2 || '',
permanentAddressHouse: permanentAddress.house || '',
permanentAddressLandmark: permanentAddress.landmark || '',
permanentAddressLocality: permanentAddress.locality || '',
permanentAddressVtc: permanentAddress.vtc || '',
permanentAddressDistrict: permanentAddress.district || '',
permanentAddressPin: permanentAddress.pin || '',
permanentAddressState: permanentAddress.state || '',
permanentAddressCountry: permanentAddress.country || '',
licenseTypes: licenseTypes.map((item: { $: { abbreviation: string } }) => item.$.abbreviation).join(', '),
gender: issuedTo?.$?.gender || '',
photo: issuedTo?.Photo?.[0]._ || ''
}
return drivingLicenseData
} catch (error) {
return { error: 'Error parsing Driving License XML. Please check the input data.' }
}
}
5 changes: 2 additions & 3 deletions packages/digilocker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export const test11 = () => {
return 'digilocker'
}
export * from './digilocker'
export * from './digilockerDataParse'
Loading

0 comments on commit c222724

Please sign in to comment.