-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add digiLocker module #31
Changes from 2 commits
5c785e2
c32fc3d
c377dc8
937d15a
52b6c9d
04a5de1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
node_modules | ||
.DS_Store | ||
build | ||
build | ||
|
||
*.tgz |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"name": "@adeya/digilocker", | ||
"version": "0.0.0", | ||
"license": "Apache-2.0", | ||
"main": "build/index", | ||
"source": "src/index", | ||
"homepage": "https://github.com/credebl/adeya-sdk/tree/main/packages/digilocker", | ||
"repository": { | ||
"url": "https://github.com/credebl/adeya-sdk/tree/main/packages/digilocker", | ||
"type": "git", | ||
"directory": "packages/digilocker" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"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", | ||
"clean": "rimraf -rf ./build", | ||
"compile": "tsc", | ||
"release": "release-it" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^18.18.8", | ||
"rimraf": "3.0.2", | ||
"typescript": "~5.5.2", | ||
"@types/xml2js": "~0.4.14", | ||
"@types/uuid": "^9.0.2" | ||
}, | ||
"peerDependencies": { | ||
"react-native": "0.72.5" | ||
} | ||
} |
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` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we those API URL and config in const file? |
||
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' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||
|
||
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}` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||
|
||
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}` } | ||
} | ||
} |
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.' } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './digilocker' | ||
export * from './digilockerDataParse' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "./build" | ||
}, | ||
"include": ["src"] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep
react-native
only in the peer dependencies