POST https://example.com/api/ownid/verifyIdDoc
Always validate the
ownid-signature and ownid-timestamp headers before trusting the request body. See Validating Signatures for the validation process.Request Body
The request body includes the user identifier and the extracted document data.- Passport
- Driver's License
{
"loginId": "user@example.com",
"type": "PASSPORT",
"data": {
"number": "X12345678",
"firstName": "Alex",
"lastName": "Morgan",
"gender": "F",
"dateOfBirth": "1990-05-14",
"dateOfIssue": "2020-01-10",
"dateOfExpiry": "2030-01-09",
"placeOfBirth": "BRAZIL",
"nationality": "BRAZIL"
}
}
{
"loginId": "user@example.com",
"type": "DRIVERLICENSE",
"data": {
"number": "D1234567",
"firstName": "Alex",
"lastName": "Morgan",
"gender": "F",
"dateOfBirth": "1990-05-14",
"dateOfIssue": "2021-03-01",
"dateOfExpiry": "2029-03-01"
}
}
| Field | Type | Description |
|---|---|---|
loginId | string | User identifier supplied when the challenge was started. |
type | string | Document type. One of PASSPORT or DRIVERLICENSE. |
data.number | string | Full document number. |
data.firstName | string | First name as it appears on the document. |
data.lastName | string | Last name as it appears on the document. |
data.gender | string | Gender as it appears on the document. One of F, M, or O. |
data.dateOfBirth | string | Date of birth in ISO 8601 format (YYYY-MM-DD). |
data.dateOfIssue | string | Document issue date in ISO 8601 format. |
data.dateOfExpiry | string | Document expiry date in ISO 8601 format. |
data.placeOfBirth | string | Place of birth. Passport only. |
data.nationality | string | Nationality as it appears on the document. Passport only. |
Responses
Return one of the following HTTP code responses: {{NO CONTENT}}
{
"reason": "name mismatch" // never pass PII, only for debugging purposes
}
{{NO CONTENT}}
Example Implementation
router.post('/verifyIdDoc', async (req, res) => {
validateOwnIdSignature(req); // See Validating Signatures.
const { loginId, type, data } = req.body;
const user = await users.findByLoginId(loginId);
if (!user) {
return res.status(404).send();
}
const nameMatches =
data.firstName.toLowerCase() === user.firstName.toLowerCase() &&
data.lastName.toLowerCase() === user.lastName.toLowerCase();
if (!nameMatches) {
return res.status(403).json({ reason: 'name mismatch' });
}
return res.status(204).send();
});
@PostMapping("/verifyIdDoc")
public ResponseEntity<Object> verifyIdDoc(@RequestBody VerifyIdDocRequest req) {
validateOwnIdSignature(req); // See Validating Signatures.
User user = userRepository.findByLoginId(req.getLoginId());
if (user == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
boolean nameMatches =
req.getData().getFirstName().equalsIgnoreCase(user.getFirstName()) &&
req.getData().getLastName().equalsIgnoreCase(user.getLastName());
if (!nameMatches) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(Map.of("reason", "name mismatch"));
}
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
[HttpPost("verifyIdDoc")]
public async Task<ActionResult> VerifyIdDoc([FromBody] VerifyIdDocRequest req)
{
ValidateOwnIdSignature(req); // See Validating Signatures.
var user = await _userRepository.FindByLoginIdAsync(req.LoginId);
if (user == null)
{
return NotFound();
}
var nameMatches =
string.Equals(req.Data.FirstName, user.FirstName, StringComparison.OrdinalIgnoreCase) &&
string.Equals(req.Data.LastName, user.LastName, StringComparison.OrdinalIgnoreCase);
if (!nameMatches)
{
return StatusCode(403, new { reason = "name mismatch" });
}
return NoContent();
}
@app.route('/verifyIdDoc', methods=['POST'])
def verify_id_doc():
validate_ownid_signature(request) # See Validating Signatures.
body = request.get_json()
login_id = body['loginId']
data = body['data']
user = db.session.query(User).filter(User.email == login_id).first()
if not user:
return ('', 404)
name_matches = (
data['firstName'].lower() == user.first_name.lower() and
data['lastName'].lower() == user.last_name.lower()
)
if not name_matches:
return ({'reason': 'name mismatch'}, 403)
return ('', 204)