Skip to content

Commit

Permalink
add kontrollsiffer validation for norwegian national identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
HauklandJ committed Nov 19, 2024
1 parent ad533e6 commit 94cf242
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 12 deletions.
1 change: 0 additions & 1 deletion src/layout/PersonLookup/PersonLookupComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ async function fetchPerson({
if (error.response.status === 403) {
return { person: null, error: 'person_lookup.validation_error_forbidden' };
}
// TODO: expose this status from app-lib?
if (error.response.status === 429) {
return { person: null, error: 'person_lookup.validation_error_too_many_requests' };
}
Expand Down
13 changes: 13 additions & 0 deletions src/layout/PersonLookup/validation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { checkValidSsn } from 'src/layout/PersonLookup/validation';

describe('checkValidSsn', () => {
it('should return true with valid ssn', () => {
expect(checkValidSsn('13014013525')).toBe(true);
}),
it('should return false with invalid ssn', () => {
expect(checkValidSsn('11017512345')).toBe(false);
}),
it('should return true with a tenor user', () => {
expect(checkValidSsn('66877400531')).toBe(true);
});
});
35 changes: 24 additions & 11 deletions src/layout/PersonLookup/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,42 @@ import type { Person, PersonLookupResponse } from 'src/layout/PersonLookup/Perso
const ajv = new Ajv({ allErrors: true });
addErrors(ajv);

/**
* @see https://github.com/navikt/k9-punsj-frontend/blob/435a445a14797dee5c19fb1c1a70c323c3f4187c/src/app/rules/IdentRules.ts
*/
const REGEX_FNR =
/^((((0[1-9]|[12]\d|30)(0[469]|11)|(0[1-9]|[12]\d|3[01])(0[13578]|1[02])|((0[1-9]|1\d|2[0-8])02))\d{2})|2902([02468][048]|[13579][26]))\d{5}$/;
/**
* @see https://github.com/navikt/k9-punsj-frontend/blob/435a445a14797dee5c19fb1c1a70c323c3f4187c/src/app/rules/IdentRules.ts
*/
const REGEX_DNR =
/^((((4[1-9]|[56]\d|70)(0[469]|11)|(4[1-9]|[56]\d|7[01])(0[13578]|1[02])|((4[1-9]|5\d|6[0-8])02))\d{2})|6902([02468][048]|[13579][26]))\d{5}$/;
ajv.addKeyword({
keyword: 'isValidSsn',
type: 'string',
validate: (_, data: string) => {
if (typeof data !== 'string') {
return false;
}

return checkValidSsn(data);
},
});

const ssnSchema: JSONSchemaType<Pick<Person, 'ssn'>> = {
type: 'object',
properties: {
ssn: {
type: 'string',
pattern: new RegExp(`((${REGEX_FNR.source}))|((${REGEX_DNR.source}))`).source,
isValidSsn: true,
errorMessage: 'person_lookup.validation_error_ssn',
},
},
required: ['ssn'],
};

export function checkValidSsn(ssn: string): boolean {
if (ssn.length !== 11) {
return false;
}
const [d1, d2, m1, m2, y1, y2, i1, i2, i3, k1, k2] = ssn.split('').map(Number);
const calculated_k1 = 11 - ((3 * d1 + 7 * d2 + 6 * m1 + 1 * m2 + 8 * y1 + 9 * y2 + 4 * i1 + 5 * i2 + 2 * i3) % 11);
const calculated_k2 =
11 - ((5 * d1 + 4 * d2 + 3 * m1 + 2 * m2 + 7 * y1 + 6 * y2 + 5 * i1 + 4 * i2 + 3 * i3 + 2 * calculated_k1) % 11);

return k1 === calculated_k1 && k2 === calculated_k2;
}

const nameSchema: JSONSchemaType<Pick<Person, 'name'>> = {
type: 'object',
properties: {
Expand Down

0 comments on commit 94cf242

Please sign in to comment.