Last active
August 11, 2021 07:19
-
-
Save R-Peleg/e724613b1b1b2c36fff045750e04cc89 to your computer and use it in GitHub Desktop.
Veriff basic client for nodejs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Integration with Veriff's API | |
import axios, {Method} from 'axios'; | |
import {createHmac} from 'crypto'; | |
import express from 'express'; | |
import bodyParser from 'body-parser'; | |
import http from 'http'; | |
type VeriffStartSessionRequest = { | |
/** | |
* default is defined on settings - Callback url to which the | |
* client is redirected after the verification session is completed | |
*/ | |
callback?: string; | |
/** Person to be verified */ | |
person?: { | |
/** First name */ | |
firstName: string | |
/** Last name */ | |
lastName: string | |
/** National identification number */ | |
idNumber: string | |
/** Gender */ | |
gender?: string | |
/** (YYYY-MM-DD) Date of birth */ | |
dateOfBirth?: string | |
}; | |
/** Document of a person to be verified */ | |
document?: { | |
/** Document number */ | |
number: string; | |
/** ISO-2 - String Issuing country of the document */ | |
country: string; | |
/** Document type */ | |
type: | |
'PASSPORT' | | |
'ID_CARD' | | |
'DRIVERS_LICENSE' | | |
'RESIDENCE_PERMIT'; | |
}; | |
/** Vendor specific data string, max 400 characters long, will be sent back unmodified using webhooks */ | |
vendorData?: string | |
/** | |
* (required) - Combined ISO 8601 date and time in UTC | |
* (YYYY-MM-DDTHH:MM:S+Timezone Offset|Z, i.e., 2018-04-18T11:02:05.261Z) | |
*/ | |
timestamp: string; | |
} | |
type VeriffFailureResponse = { | |
status: 'fail', | |
code: string, | |
message: string | |
}; | |
type VeriffStartSessionResponse = { | |
status: 'success', | |
verification: { | |
id: string; | |
url: string; | |
status: string; | |
sessionToken: string; | |
} | |
} | VeriffFailureResponse; | |
type VeriffUploadMediaRequest = { | |
/** (required) Type of a document (face|document-front|document-back) */ | |
context: 'face' | 'document-front' | 'document-back'; | |
/** | |
* base64 encoded image (png|jpg|jpeg) | |
* Example data:image/png;base64,R0lGODlhAQABAAAAACw= | |
*/ | |
content: string; | |
/** | |
* (required) Combined ISO 8601 date and time in UTC | |
* (YYYY-MM-DDTHH:MM:S+Timezone Offset|Z, i.e., 2018-04-18T11:02:05.261Z) | |
*/ | |
timestamp: string; | |
} | |
type VeriffUplaodMediaResponse = { | |
status: 'success', | |
image: { | |
/** Type of an image */ | |
context: string; | |
/** UUID-v4 String UUID v4 which identifies the image */ | |
id: string; | |
name: string; | |
/** Object Timestamp object, deprecated, will return null/None */ | |
timestamp: null; | |
size: number; | |
mimetype: string; | |
url: string; | |
} | |
} | VeriffFailureResponse; | |
type VeriffSubmitResponse = { | |
status: 'success'; | |
} | VeriffFailureResponse; | |
export class VeriffClient { | |
apiPublic: string; | |
apiSecret: string; | |
baseUrl: string; | |
constructor(apiPublic: string, apiSecret: string, baseUrl: string = 'https://stationapi.veriff.com') { | |
this.apiPublic = apiPublic; | |
this.apiSecret = apiSecret; | |
this.baseUrl = baseUrl; | |
} | |
private async veriffRequest<ResponseType extends object> | |
(request: object, method: Method, path: string, sign: boolean = true) : Promise<ResponseType> { | |
const body = JSON.stringify(request); | |
const signatrueHeader = sign ? { | |
'X-HMAC-SIGNATURE': createHmac('sha256', this.apiSecret).update(body, 'utf8').digest('hex') | |
} : {} | |
const headers = { | |
'Content-Type': 'application/json', | |
'X-AUTH-CLIENT': this.apiPublic, | |
...signatrueHeader | |
}; | |
const response = await axios.request({ | |
method, | |
url: this.baseUrl + path, | |
headers, | |
data: body, | |
validateStatus: () => true, | |
}); | |
return response.data; | |
} | |
startSession(request: VeriffStartSessionRequest) : Promise<VeriffStartSessionResponse> { | |
return this.veriffRequest({verification: request}, 'POST', '/v1/sessions', false); | |
} | |
uploadMedia(sessionId: string, request: VeriffUploadMediaRequest) : Promise<VeriffUplaodMediaResponse> { | |
return this.veriffRequest({image: request}, 'POST', '/v1/sessions/' + sessionId + '/media'); | |
} | |
submitSession(sessionId: string) : Promise<VeriffSubmitResponse> { | |
const request = {verification: { | |
timestamp: (new Date()).toISOString(), | |
status: 'submitted' | |
}}; | |
return this.veriffRequest(request, 'PATCH', '/v1/sessions/' + sessionId); | |
} | |
} | |
type VeriffWebhookDecision = { | |
status: 'approved' | 'resubmission_requested' | 'declined' | 'expired' | 'abandoned'; | |
verification: { | |
id: string; | |
status: 'approved' | 'resubmission_requested' | 'declined' | 'expired' | 'abandoned'; | |
code: 9001 | 9102 | 9103 | 9104; | |
reason: string; | |
person: { | |
firstName: string; | |
lastName: string; | |
idNumber: string; | |
/** (Deprecated) ISO-2 String Citizenship */ | |
citizenship: null; | |
/** (YYYY-MM-DD) Date of birth */ | |
dateOfBirth: string; | |
nationality: string; | |
pepSanctionMatch?: string; | |
gender: 'M' | 'F' | null; | |
yearOfBirth: string; | |
placeOfBirth: string; | |
}; | |
document: { | |
number: string; | |
type: 'PASSPORT' | 'ID_CARD' | 'DRIVERS_LICENSE' | 'RESIDENCE_PERMIT' | 'OTHER'; | |
/** ISO-2 String Document country */ | |
country: string; | |
validFrom: string; | |
validUntil: string; | |
}; | |
additionalVerifiedData?: { | |
driversLicenseCategory?: { | |
B: boolean | null; | |
}; | |
}; | |
vendorData: string; | |
reasonCode: number; | |
decisionTime: string; | |
acceptanceTime: string; | |
riskLabels: { | |
label: string; | |
category: 'client_data_mismatch' | 'crosslinks' | 'device' | 'document' | |
| 'images' | 'network' | 'session' | 'person'; | |
}[] | |
}; | |
technicalData: { | |
ip: string; | |
} | |
}; | |
type VeriffWebhookEvent = { | |
/** UUID v4 which identifies the verification session */ | |
id: string; | |
/** UUID v4 which identifies session attempt */ | |
attemptId: string; | |
/** Feature on which the event was triggered (selfid refers to the end user flow) */ | |
feature: string; | |
/** Event code (one of 7001, 7002) */ | |
code: string; | |
/** Corresponding action description */ | |
action: 'started' | 'submitted'; | |
/** Vendor specific data string set during session creation */ | |
vendorData?: string; | |
}; | |
/** Create an Express application for */ | |
export function createWebhooksApp( | |
privateApiKey: string, | |
onDecision: (decision: VeriffWebhookDecision) => void, | |
onEvent: (event: VeriffWebhookEvent) => void | |
) : express.Express { | |
const app = express(); | |
app.use(bodyParser.text({'type': '*/*'})); | |
// Verify signature to ensure nobody tries to mess with us | |
app.use((req, res, next) => { | |
const expectedSignature = createHmac('sha256', privateApiKey).update(req.body, 'utf8').digest('hex'); | |
const signature = req.headers['x-hmac-signature']; | |
if (expectedSignature !== signature) { | |
console.warn('Invalid auth, expected ' + expectedSignature); | |
res.status(400); | |
res.send('Invalid authentication'); | |
return; | |
} | |
// Parse JSON for application | |
if (req.headers['content-type'] === 'application/json') { | |
req.body = JSON.parse(req.body); | |
} | |
next(); | |
}); | |
app.post('/decision', (req, res) => { | |
onDecision(req.body); | |
res.send({}); | |
}); | |
app.post('/event', (req, res) => { | |
onEvent(req.body); | |
res.send({}); | |
}); | |
return app; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment