-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample_backend.js
More file actions
208 lines (184 loc) · 6.56 KB
/
example_backend.js
File metadata and controls
208 lines (184 loc) · 6.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
const apiurl = import.meta.env.VITE_FAKE_BACKEND_APIURL;
const flowid = import.meta.env.VITE_FAKE_BACKEND_FLOWID;
const apikey = import.meta.env.VITE_FAKE_BACKEND_APIKEY;
const defaultHeader = {
"Content-Type": "application/json",
"x-api-key": apikey,
"api-version": "1.0",
};
// Public: Call Incode's `omni/start` API to create an Incode session which will include a
// token in the response.
const start = async function (identityId) {
const url = `${apiurl}/omni/start`;
const params = {
configurationId: flowid,
// language: "en-US",
// redirectionUrl: "https://example.com?custom_parameter=some+value",
// externalCustomerId: "the id of the customer in your system",
};
let response;
try {
response = await fetch(url, { method: "POST", body: JSON.stringify(params), headers: defaultHeader });
if (!response.ok) {
throw new Error("Request failed with code " + response.status);
}
} catch (e) {
throw new Error("HTTP Post Error: " + e.message);
}
// The session response has many values, but you should only pass the token to the frontend.
const responseData = await response.json();
const { token } = responseData;
return { token };
};
// Public: Verify the authentication by checking the score and idenitityId returned by the backend, and comparing it with the candidate returned by renderFaceAuth.
const getResults = async function (token, candidate) {
// Finishing the session triggers score calculation and business rules.
await finishStatus(token); // Mark session as finished in Incode backend
// Closing the session stops it from being changed, all /add/ endpoints will be rejected after this, and the score will be frozen.
await setStatusClosed(token); // Mark session as closed in Incode backend
let identityId, scoreStatus;
try {
const scoreResponse = await getScore(token);
identityId = scoreResponse.authentication.identityId;
scoreStatus = scoreResponse.overall.status;
} catch (e) {
// If there is an error communicating with API, we consider validation failed.
return {
// Detailed debug message, in production you might want to avoid exposing internal details.
message: "Error validating authentication: " + e.message,
isValid: false,
};
}
// renderFaceAuth returns candidate, which should match identityId from score,
// this prevents tampering of the identityId in the frontend.
if (identityId !== candidate) {
return {
// Detailed debug message, in production you might want to avoid exposing internal details.
message: "candidate " + candidate + " does not match identityId " + identityId + " from score",
isValid: false,
};
}
// If backend score overall status is not OK, validation fails.
if (scoreStatus !== "OK") {
return {
// Detailed debug message, in production you might want to avoid exposing internal details.
message: "Face Validation failed for candidate " + candidate,
isValid: false,
};
}
// Only valid if all checks passed, we return the identityId that was validated.
return {
// Detailed debug message, in production you might want to avoid exposing internal details.
message: "Face Validation succeeded for candidate " + candidate,
isValid: true,
identityId: identityId,
};
};
// Private: Calls Incode's `/0/omni/finish-status` API to mark the session as finished
const finishStatus = async function (token) {
const url = `${apiurl}/0/omni/finish-status`;
let sessionHeaders = { ...defaultHeader };
sessionHeaders["X-Incode-Hardware-Id"] = token;
let response;
try {
response = await fetch(url, { method: "POST", body: JSON.stringify({}), headers: sessionHeaders });
if (!response.ok) {
throw new Error("Request failed with code " + response.status);
}
} catch (e) {
throw new Error("HTTP Post Error: " + e.message);
}
const { redirectionUrl, action } = await response.json();
return { redirectionUrl, action };
};
// Private: Calls Incode's `omni/session/status/set?action=Closed` API to close the session
const setStatusClosed = async function (token) {
const url = `${apiurl}/omni/session/status/set?action=Closed`;
let sessionHeaders = { ...defaultHeader };
sessionHeaders["X-Incode-Hardware-Id"] = token;
let response;
try {
response = await fetch(url, { method: "POST", body: JSON.stringify({}), headers: sessionHeaders });
if (!response.ok) {
throw new Error("Request failed with code " + response.status);
}
} catch (e) {
throw new Error("HTTP Post Error: " + e.message);
}
const {sessionStatus} = await response.json();
/* Example response
{
"_id": "69c5c01ac40764536244ac3b",
"_createdAt": 1774567450715,
"_updatedAt": 1774567469629,
"closedAt": 1774567469629,
"sessionStatus": "Closed"
}
*/
return {sessionStatus};
};
// Private: Call Incode's `omni/get/score` API to retrieve the score for the session
const getScore = async function (token) {
const url = `${apiurl}/0/omni/get/score`;
let sessionHeaders = { ...defaultHeader };
sessionHeaders["X-Incode-Hardware-Id"] = token;
let response;
try {
response = await fetch(url, { method: "GET", headers: sessionHeaders });
if (!response.ok) {
throw new Error("Request failed with code " + response.status);
}
} catch (e) {
throw new Error("HTTP Post Error: " + e.message);
}
const score = await response.json();
/* Example score
{
"authentication": {
"overall": {
"value": "89.2",
"status": "OK"
},
"identityId": "68c851bedd5176f7d8bf4758"
},
"liveness": {
"physicalAttack": {
"value": "100.0",
"status": "OK"
},
"spoofDetectionMethod": "SF",
"overall": {
"value": "100.0",
"status": "OK"
},
"livenessScore": {
"value": "100.0",
"status": "OK"
}
},
"deviceRisk": {
"overall": {
"status": "UNKNOWN"
}
},
"behavioralRisk": {
"overall": {
"status": "UNKNOWN"
}
},
"retryInfo": {},
"documentOnEdgeInfo": {},
"sessionRecording": {
"mergedRecordingQualityChecks": {}
},
"reasonMsg": "This session passed because it passed all of Incode's tests: Liveness Detection",
"overall": {
"value": "94.6",
"status": "OK"
}
}
*/
return score;
};
const exampleBackend = { start, getResults }
export default exampleBackend;