diff --git a/frontend/src/routes/guest/register/steps/register.tsx b/frontend/src/routes/guest/register/steps/register.tsx
index 86f52bd241962c59cc61df154f82ce69a3af1430..0499c4b6f557437570b007f992a066dd048963f9 100644
--- a/frontend/src/routes/guest/register/steps/register.tsx
+++ b/frontend/src/routes/guest/register/steps/register.tsx
@@ -590,7 +590,7 @@ const GuestRegisterStep = forwardRef(
                   // It is not required that the Norwegian national ID number be filled in, the guest may not have
                   // one, so allow empty values for the validation to pass. Note that both "fødselsnummer" and
                   // D-number are allowed as input, and in some cases SO-number
-                  validate: (value) => isValidFnr(value, true),
+                  validate: (value) => isValidFnr(value, true, true),
                 }}
                 render={({ field }) => (
                   <TextField
diff --git a/frontend/src/utils/index.test.ts b/frontend/src/utils/index.test.ts
index bdc3108c327e6449021557ba70ed5c9155aec2c9..e297ab291aa12e12b0fc86a1528b35662730078e 100644
--- a/frontend/src/utils/index.test.ts
+++ b/frontend/src/utils/index.test.ts
@@ -6,6 +6,7 @@ import {
   isValidEmail,
   isValidFirstName,
   isValidFnr,
+  isValidSoNumber,
   isValidLastName,
   isValidMobilePhoneNumber,
   maybeCsrfToken,
@@ -167,6 +168,14 @@ test('Valid fnr', async () => {
   expect(isValidFnr('04026514903')).toEqual(true)
 })
 
+test('SO number validation', async () => {
+  expect(isValidSoNumber('22522218883')).toEqual(true)
+  expect(isValidSoNumber('22022218883')).toEqual(false)
+
+  expect(isValidFnr('22522218883')).toEqual("common:validation.invalidIdNumber")
+  expect(isValidFnr('22522218883', false, true)).toEqual(true)
+})
+
 test('Null fnr', async () => {
   expect(isValidFnr(undefined)).toEqual('common:validation.invalidIdNumber')
 })
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index 90854e450499987ff83b45959015cbf070b5ad7c..8ba54d2dc207529325a2c142ac1f9c73aea00e14 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -73,9 +73,83 @@ export function fetchJsonOpts(): RequestInit {
   }
 }
 
+export function norwegianIdNumberHasValidChecksum(digits: number[]): boolean {
+  // from https://github.com/navikt/fnrvalidator/blob/master/src/validator.js
+  let k1 =
+    11 -
+    ((3 * digits[0] +
+      7 * digits[1] +
+      6 * digits[2] +
+      1 * digits[3] +
+      8 * digits[4] +
+      9 * digits[5] +
+      4 * digits[6] +
+      5 * digits[7] +
+      2 * digits[8]) %
+      11)
+  let k2 =
+    11 -
+    ((5 * digits[0] +
+      4 * digits[1] +
+      3 * digits[2] +
+      2 * digits[3] +
+      7 * digits[4] +
+      6 * digits[5] +
+      5 * digits[6] +
+      4 * digits[7] +
+      3 * digits[8] +
+      2 * k1) %
+      11)
+
+  if (k1 === 11) k1 = 0
+  if (k2 === 11) k2 = 0
+
+  return k1 < 10 && k2 < 10 && k1 === digits[9] && k2 === digits[10]
+}
+
+export function isValidSoNumber(data: string): boolean {
+  // see https://lovas.info/2013/12/01/identitetsnummer-i-norge/
+
+  const day = data.slice(0, 2)
+  const month = data.slice(2, 4)
+  const year = data.slice(4, 6)
+  const pnr = data.slice(6, 11)
+
+  // this part should always start with 1
+  if (pnr.slice(0, 1) !== '1') {
+    return false
+  }
+
+  const actualMonth = parseInt(month) - 50
+  const date = new Date(
+    year === '00' ? 2000 : parseInt(year),
+    actualMonth - 1,
+    parseInt(day)
+  )
+
+  // date must be a valid date
+  if (
+    !(
+      date &&
+      date.getMonth() + 1 === actualMonth &&
+      date.getDate() === parseInt(day)
+    )
+  ) {
+    return false
+  }
+
+  // checksum must match
+  const digits = data.split('').map((x) => parseInt(x))
+  if (!norwegianIdNumberHasValidChecksum(digits)) {
+    return false
+  }
+  return true
+}
+
 export function isValidFnr(
   data: string | undefined,
-  allowEmpty = false
+  allowEmpty = false,
+  allowSoNumber = false,
 ): boolean | string {
   if (!data) {
     if (allowEmpty) {
@@ -83,7 +157,12 @@ export function isValidFnr(
     }
     return i18n.t<string>('common:validation.invalidIdNumber').toString()
   }
-  if (validator.idnr(data as string).status === 'valid') {
+  const validation = validator.idnr(data as string)
+  if (validation.status === 'valid' && ["fnr", "dnr"].includes(validation.type)) {
+    return true
+  }
+  if (allowSoNumber && isValidSoNumber(data)) {
+
     return true
   }
   // TypeScript complains if toString is not used on the function result