diff --git a/docs b/docs
index 97b8ef607..3cc94e532 160000
--- a/docs
+++ b/docs
@@ -1 +1 @@
-Subproject commit 97b8ef607aa78cc38d04c030fabd8d55806cd1df
+Subproject commit 3cc94e532274ee194ac05edb33b76980e415b3dd
diff --git a/src/app/_components/OmegaId/identification/OmegaIdElement.module.scss b/src/app/_components/OmegaId/identification/OmegaIdElement.module.scss
index 9f94b52d5..811707e85 100644
--- a/src/app/_components/OmegaId/identification/OmegaIdElement.module.scss
+++ b/src/app/_components/OmegaId/identification/OmegaIdElement.module.scss
@@ -2,8 +2,4 @@
.OmegaIdElement {
max-width: 400px;
- > p {
- width: 100%;
- text-align: center;
- }
-}
\ No newline at end of file
+}
diff --git a/src/app/_components/OmegaId/identification/OmegaIdElement.tsx b/src/app/_components/OmegaId/identification/OmegaIdElement.tsx
index 7ca89981c..4f3098c14 100644
--- a/src/app/_components/OmegaId/identification/OmegaIdElement.tsx
+++ b/src/app/_components/OmegaId/identification/OmegaIdElement.tsx
@@ -2,27 +2,20 @@
import styles from './OmegaIdElement.module.scss'
import { generateOmegaIdAction } from '@/actions/omegaid/generate'
import { readJWTPayload } from '@/jwt/jwtReadUnsecure'
+import { compressOmegaId } from '@/services/omegaid/compress'
import { useQRCode } from 'next-qrcode'
import { useEffect, useState } from 'react'
const EXPIRY_THRESHOLD = 60
-type PropTypes = {
+export default function OmegaIdElement({ token }: {
token: string,
-}
-
-export default function OmegaIdElement({ token }: PropTypes) {
+}) {
const [tokenState, setTokenState] = useState(token)
const { SVG } = useQRCode()
- const JWTPayload = readJWTPayload<{
- gn?: string,
- sn?: string,
- }>(token)
-
- const firstname = JWTPayload.gn ?? ''
- const lastname = JWTPayload.sn ?? ''
+ const JWTPayload = readJWTPayload(tokenState)
const [expiryTime, setExpiryTime] = useState(new Date((JWTPayload.exp - EXPIRY_THRESHOLD) * 1000))
@@ -48,9 +41,7 @@ export default function OmegaIdElement({ token }: PropTypes) {
return
-
-
{firstname} {lastname}
}
diff --git a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
index 70e0ce1fd..14fd9998d 100644
--- a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
+++ b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
@@ -6,6 +6,7 @@ import { Html5QrcodeScanner } from 'html5-qrcode'
import { useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'
import type { OmegaId } from '@/services/omegaid/Types'
+import { decomporessOmegaId as decompressOmegaId } from '@/services/omegaid/compress'
/**
* Renders a component for reading OmegaId QR codes.
@@ -51,11 +52,17 @@ export default function OmegaIdReader({
let lastReadTime = 0
let lastReadUserId = -1
- html5QrcodeScanner.render(async (token) => {
- const parse = await parseJWT(token, publicKey, expiryOffset ?? 100)
+ html5QrcodeScanner.render(async (rawToken) => {
+ const token = decompressOmegaId(rawToken)
+ if (!token.success) {
+ setFeedBack({
+ status: 'ERROR',
+ text: 'Ugyldig QR kode'
+ })
+ return
+ }
+ const parse = await parseJWT(token.data, publicKey, expiryOffset ?? 100, 'omegaid')
if (!parse.success) {
- console.log(parse)
-
const msg = parse.error?.map(e => e.message).join(' / ') ?? 'Ukjent feil'
setFeedBack({
diff --git a/src/app/users/[username]/page.module.scss b/src/app/users/[username]/page.module.scss
index 69bae29a8..966cc0b7f 100644
--- a/src/app/users/[username]/page.module.scss
+++ b/src/app/users/[username]/page.module.scss
@@ -66,8 +66,8 @@
}
.omegaId {
- min-width: 50vw;
- min-height: 50vw;
+ min-width: min(50vw, 400px);
+ min-height: min(50vw, 400px);
display: grid;
place-items: center;
> * {
diff --git a/src/app/users/[username]/page.tsx b/src/app/users/[username]/page.tsx
index 8cddfcc63..0e93cfc03 100644
--- a/src/app/users/[username]/page.tsx
+++ b/src/app/users/[username]/page.tsx
@@ -47,6 +47,8 @@ export default async function User({ params }: PropTypes) {
{ username: profile.user.username }
).auth(session)
+ const showOmegaId = session.user?.username === params.username
+
return (
@@ -57,7 +59,7 @@ export default async function User({ params }: PropTypes) {
{`${profile.user.firstname} ${profile.user.lastname}`}
-
@@ -67,7 +69,7 @@ export default async function User({ params }: PropTypes) {
-
+ }
{
studyProgramme && (
diff --git a/src/jwt/jwt.ts b/src/jwt/jwt.ts
index beee35138..d9eec9036 100644
--- a/src/jwt/jwt.ts
+++ b/src/jwt/jwt.ts
@@ -29,7 +29,7 @@ export function generateJWT
(
throw new ServerError('INVALID CONFIGURATION', 'Missing secret for JWT generation')
}
- return sign(payload, asymetric ? process.env.JWT_PRIVATE_KEY : process.env.NEXTAUTH_SECRET, {
+ return sign(payload, Buffer.from(asymetric ? process.env.JWT_PRIVATE_KEY : process.env.NEXTAUTH_SECRET), {
audience: aud,
algorithm: asymetric ? 'ES256' : 'HS256',
issuer: JWT_ISSUER,
diff --git a/src/services/omegaid/ConfigVars.ts b/src/services/omegaid/ConfigVars.ts
index 712955b03..dfad0f692 100644
--- a/src/services/omegaid/ConfigVars.ts
+++ b/src/services/omegaid/ConfigVars.ts
@@ -1,11 +1,2 @@
-import type { UserFiltered } from '@/services/users/Types'
-
-
-export const omegaIdFields = [
- 'id',
- 'firstname',
- 'lastname',
- 'username',
-] as const satisfies (keyof UserFiltered)[]
export const OmegaIdExpiryTime = 60 * 5 // 5 minutes
diff --git a/src/services/omegaid/Types.ts b/src/services/omegaid/Types.ts
index 981fe9f8b..3d82cbe70 100644
--- a/src/services/omegaid/Types.ts
+++ b/src/services/omegaid/Types.ts
@@ -1,11 +1,9 @@
-import type { omegaIdFields } from './ConfigVars'
import type { UserFiltered } from '@/services/users/Types'
-export type OmegaId = Pick
+export type OmegaId = Pick
export type OmegaIdJWT = {
+ iat: number,
+ exp: number,
sub: UserFiltered['id'],
- usrnm: UserFiltered['username'],
- gn: UserFiltered['firstname'],
- sn: UserFiltered['lastname'],
}
diff --git a/src/services/omegaid/compress.ts b/src/services/omegaid/compress.ts
new file mode 100644
index 000000000..6f32b56fd
--- /dev/null
+++ b/src/services/omegaid/compress.ts
@@ -0,0 +1,110 @@
+import { JWT_ISSUER } from '@/auth/ConfigVars'
+import type { ActionReturn, ActionReturnError } from '@/actions/Types'
+import type { OmegaIdJWT } from '@/services/omegaid/Types'
+
+/**
+ * This file handles compression and decompression of omegaID
+ */
+export function compressOmegaId(token: string): string {
+ const parts = token.split('.')
+ const payloadCompressed = compressPayload(parts[1])
+ const payload = base64ToBigInt(payloadCompressed)
+ const sign = base64ToBigInt(parts[2])
+ const ret = `${payload}.${sign}`
+ return ret
+}
+
+function decodeBase64Url(base64: string) {
+ return atob(base64.replaceAll('-', '+').replaceAll('_', '/'))
+}
+
+function encodeBase64Url(data: string): string {
+ return btoa(data).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '')
+}
+
+function base64ToBigInt(base64: string): string {
+ const binaryString = decodeBase64Url(base64)
+ let bigint = BigInt(0)
+
+ // Convert binary string to BigInt
+ for (let i = 0; i < binaryString.length; i++) {
+ bigint = (bigint << BigInt(8)) | BigInt(binaryString.charCodeAt(i))
+ }
+
+ return bigint.toString()
+}
+
+function bigIntToBase64(bigIntString: string): string {
+ let bigInt = BigInt(bigIntString)
+ let binaryString = ''
+
+ while (bigInt > BigInt(0)) {
+ const nextChar = bigInt & BigInt(0xFF)
+ binaryString += String.fromCharCode(Number(nextChar))
+ bigInt >>= BigInt(8)
+ }
+
+ binaryString = binaryString.split('').reverse().join('')
+
+ return encodeBase64Url(binaryString)
+}
+
+function compressPayload(payload: string): string {
+ const binaryString = decodeBase64Url(payload)
+ const payloadString = binaryString.toString()
+ const payloadJSON = JSON.parse(payloadString) as OmegaIdJWT
+ const shortPayloadString = `${payloadJSON.sub},${payloadJSON.iat},${payloadJSON.exp}`
+ return encodeBase64Url(shortPayloadString)
+}
+
+function decompressPayload(rawdata: string): string {
+ const base64String = bigIntToBase64(rawdata)
+ const byteString = decodeBase64Url(base64String)
+ const dataString = byteString.toString().split(',')
+ const payload = {
+ sub: Number(dataString[0]),
+ iat: Number(dataString[1]),
+ exp: Number(dataString[2]),
+ aud: 'omegaid',
+ iss: JWT_ISSUER,
+ }
+ const payloadString = JSON.stringify(payload)
+
+ return encodeBase64Url(payloadString)
+}
+
+export function decomporessOmegaId(rawdata: string): ActionReturn {
+ const header = {
+ alg: 'ES256',
+ typ: 'JWT'
+ }
+ const headerJSONString = JSON.stringify(header)
+ const headerB64String = encodeBase64Url(headerJSONString)
+
+ const errorReturn: ActionReturnError = {
+ success: false,
+ errorCode: 'JWT INVALID',
+ httpCode: 400,
+ error: [{
+ message: 'QR code is not an OmegaId',
+ }],
+ }
+
+ const rawDataSplit = rawdata.split('.')
+ if (rawDataSplit.length !== 2) {
+ return errorReturn
+ }
+
+ try {
+ const payload = decompressPayload(rawDataSplit[0])
+
+ const signature = bigIntToBase64(rawDataSplit[1])
+
+ return {
+ success: true,
+ data: `${headerB64String}.${payload}.${signature}`,
+ }
+ } catch {
+ return errorReturn
+ }
+}
diff --git a/src/services/omegaid/generate.ts b/src/services/omegaid/generate.ts
index b05f3b141..6a6377016 100644
--- a/src/services/omegaid/generate.ts
+++ b/src/services/omegaid/generate.ts
@@ -1,15 +1,12 @@
import 'server-only'
import { OmegaIdExpiryTime } from './ConfigVars'
import { generateJWT } from '@/jwt/jwt'
-import type { OmegaId, OmegaIdJWT } from './Types'
+import type { OmegaId } from './Types'
export function generateOmegaId(user: OmegaId): string {
- const payload: OmegaIdJWT = {
+ const payload = {
sub: user.id,
- usrnm: user.username,
- gn: user.firstname,
- sn: user.lastname,
}
return generateJWT('omegaid', payload, OmegaIdExpiryTime, true)