From aa1822ca864243b7a5e9d1c89ba44a4c3c5e059c Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 19:44:37 +0100
Subject: [PATCH 1/8] feat: Add compression to omegaID
---
docs | 2 +-
.../identification/OmegaIdElement.module.scss | 6 +-
.../OmegaId/identification/OmegaIdElement.tsx | 19 +--
.../OmegaId/reader/OmegaIdReader.tsx | 15 ++-
src/app/users/[username]/page.module.scss | 4 +-
src/app/users/[username]/page.tsx | 6 +-
src/jwt/jwt.ts | 2 +-
src/services/omegaid/ConfigVars.ts | 9 --
src/services/omegaid/Types.ts | 8 +-
src/services/omegaid/compress.ts | 110 ++++++++++++++++++
src/services/omegaid/generate.ts | 7 +-
11 files changed, 140 insertions(+), 48 deletions(-)
create mode 100644 src/services/omegaid/compress.ts
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)
From 39012ded3d6d1730faa71ebfe1f72491c6e192e9 Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 20:04:42 +0100
Subject: [PATCH 2/8] fix: Admision trials
---
src/actions/admission/create.ts | 5 +--
.../OmegaId/reader/OmegaIdReader.tsx | 6 ++--
.../admission/[admission]/registration.tsx | 14 ++++----
src/app/admin/admission/page.tsx | 2 +-
src/jwt/parseJWTClient.ts | 34 ++++++++-----------
src/services/admission/Types.ts | 7 ++++
src/services/admission/create.ts | 11 ++++--
7 files changed, 43 insertions(+), 36 deletions(-)
create mode 100644 src/services/admission/Types.ts
diff --git a/src/actions/admission/create.ts b/src/actions/admission/create.ts
index 8741c7b4f..fac7ef314 100644
--- a/src/actions/admission/create.ts
+++ b/src/actions/admission/create.ts
@@ -6,13 +6,14 @@ import { createAdmissionTrial } from '@/services/admission/create'
import { createAdmissionTrialValidation } from '@/services/admission/validation'
import { Session } from '@/auth/Session'
import type { ActionReturn } from '@/actions/Types'
-import type { Admission, AdmissionTrial } from '@prisma/client'
+import type { Admission } from '@prisma/client'
+import type { ExpandedAdmissionTrail } from '@/services/admission/Types'
export async function createAdmissionTrialAction(
admission: Admission,
userId: FormData | number
-): Promise> {
+): Promise> {
const session = await Session.fromNextAuth()
const authRes = CreateAdmissionTrialAuther.dynamicFields({}).auth(session)
diff --git a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
index 14fd9998d..d1ad5fe22 100644
--- a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
+++ b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
@@ -27,7 +27,7 @@ export default function OmegaIdReader({
debounceThreshold,
singleRead,
}: {
- successCallback: (user: OmegaId, token: string) => Promise<{
+ successCallback: (user: number, token: string) => Promise<{
success: boolean,
text: string,
}>,
@@ -72,7 +72,7 @@ export default function OmegaIdReader({
return
}
- const userId = parse.data.id
+ const userId = parse.data
if (userId === lastReadUserId && Date.now() - lastReadTime < (debounceThreshold ?? 5000)) {
lastReadTime = Date.now()
@@ -84,7 +84,7 @@ export default function OmegaIdReader({
text: '...',
})
- const results = await successCallback(parse.data, token)
+ const results = await successCallback(userId, token.data)
if (results.success && (singleRead ?? false)) {
html5QrcodeScanner.clear()
diff --git a/src/app/admin/admission/[admission]/registration.tsx b/src/app/admin/admission/[admission]/registration.tsx
index 021db5d85..c9b97c6c1 100644
--- a/src/app/admin/admission/[admission]/registration.tsx
+++ b/src/app/admin/admission/[admission]/registration.tsx
@@ -19,19 +19,17 @@ export default function RegisterAdmissiontrial({
Registrer med QR kode
{
- const results = await createAdmissionTrialAction(admission, user.id)
+ successCallback={async (userId) => {
+ const results = await createAdmissionTrialAction(admission, userId)
let msg = results.success ?
- `${user.firstname} er registrert` :
+ `${results.data.user.firstname} ${results.data.user.lastname} er registrert` :
'Kunne ikke regisrere bruker grunnet en ukjent feil.'
if (!results.success && results.error) {
- msg = `${user.firstname}: ${
- results.error
- .map(e => e.message)
- .reduce((acc, val) => `${acc}\n${val}`, '')
- }`
+ msg = results.error
+ .map(e => e.message)
+ .reduce((acc, val) => `${acc}\n${val}`, '')
}
return {
diff --git a/src/app/admin/admission/page.tsx b/src/app/admin/admission/page.tsx
index 5c767ed32..8d98515bc 100644
--- a/src/app/admin/admission/page.tsx
+++ b/src/app/admin/admission/page.tsx
@@ -12,7 +12,7 @@ export default async function AdmissionTrials() {
{AdmissionsArray.map(trial =>
-
- {AdmissionDisplayNames[trial]}
+ {AdmissionDisplayNames[trial]}
)}
diff --git a/src/jwt/parseJWTClient.ts b/src/jwt/parseJWTClient.ts
index 74ac2bc12..dec03f0df 100644
--- a/src/jwt/parseJWTClient.ts
+++ b/src/jwt/parseJWTClient.ts
@@ -5,7 +5,6 @@ import { JWT_ISSUER } from '@/auth/ConfigVars'
import { createActionError } from '@/actions/error'
import type { OmegaJWTAudience } from '@/auth/Types'
import type { ActionReturn } from '@/actions/Types'
-import type { OmegaId } from '@/services/omegaid/Types'
/**
* Parses a JSON Web Token (JWT) and verifies its signature using the provided public key.
@@ -16,11 +15,16 @@ import type { OmegaId } from '@/services/omegaid/Types'
* @returns A promise that resolves to an `ActionReturn` object containing the parsed JWT payload if the JWT is valid,
* or an error object if the JWT is invalid.
*/
-export async function parseJWT(token: string, publicKey: string, timeOffset: number): Promise> {
+export async function parseJWT(
+ token: string,
+ publicKey: string,
+ timeOffset: number,
+ audience: OmegaJWTAudience
+): Promise> {
// TODO: This only works in safari and firefox :///
- function invalidJWT(message?: string): ActionReturn {
- return createActionError('JWT INVALID', message || 'Ugyldig QR kode')
+ function invalidJWT(message?: string): ActionReturn {
+ return createActionError('JWT INVALID', message || 'Invalid JWT')
}
if (timeOffset < 0) {
@@ -30,7 +34,7 @@ export async function parseJWT(token: string, publicKey: string, timeOffset: num
const tokenS = token.split('.')
if (tokenS.length !== 3) {
- return invalidJWT('Ugyldig QR kode type')
+ return invalidJWT('Malformatted JWT')
}
const keyStripped = publicKey
@@ -68,35 +72,25 @@ export async function parseJWT(token: string, publicKey: string, timeOffset: num
try {
const payload = readJWTPayload(token)
- if (!(
- typeof payload.usrnm === 'string' &&
- typeof payload.gn === 'string' &&
- typeof payload.sn === 'string' &&
- typeof payload.sub === 'number'
- )) {
- return invalidJWT('Invalid fields')
+ if (typeof payload.sub !== 'number') {
+ return invalidJWT('JWT is missing sub field')
}
if (new Date(payload.exp * 1000 + timeOffset) < new Date()) {
- return invalidJWT('QR koden er utløpt')
+ return invalidJWT('JWT has expired')
}
if (payload.iss !== JWT_ISSUER) {
return invalidJWT('Invalid issuer')
}
- if (payload.aud !== 'omegaid' satisfies OmegaJWTAudience) {
+ if (payload.aud !== audience) {
return invalidJWT('Invalid audience')
}
return {
success: true,
- data: {
- id: payload.sub,
- username: payload.usrnm,
- firstname: payload.gn,
- lastname: payload.sn,
- }
+ data: payload.sub
}
} catch {
return invalidJWT('An unexpected error occured')
diff --git a/src/services/admission/Types.ts b/src/services/admission/Types.ts
new file mode 100644
index 000000000..097d1de8b
--- /dev/null
+++ b/src/services/admission/Types.ts
@@ -0,0 +1,7 @@
+import type { AdmissionTrial } from '@prisma/client'
+import type { UserFiltered } from '@/services/users/Types'
+
+
+export type ExpandedAdmissionTrail = AdmissionTrial & {
+ user: UserFiltered
+}
diff --git a/src/services/admission/create.ts b/src/services/admission/create.ts
index 58318f356..507a67ab8 100644
--- a/src/services/admission/create.ts
+++ b/src/services/admission/create.ts
@@ -4,12 +4,14 @@ import { readUserAdmissionTrials } from './read'
import { prismaCall } from '@/services/prismaCall'
import { updateUserOmegaMembershipGroup } from '@/services/groups/omegaMembershipGroups/update'
import prisma from '@/prisma'
-import { Admission, type AdmissionTrial } from '@prisma/client'
+import { userFilterSelection } from '@/services/users/ConfigVars'
+import { Admission } from '@prisma/client'
import type { CreateAdmissionTrialType } from './validation'
+import type { ExpandedAdmissionTrail } from './Types'
export async function createAdmissionTrial(
data: CreateAdmissionTrialType['Detailed']
-): Promise {
+): Promise {
const parse = createAdmissionTrialValidation.detailedValidate(data)
const results = await prismaCall(() => prisma.admissionTrial.create({
@@ -25,6 +27,11 @@ export async function createAdmissionTrial(
},
},
admission: parse.admission,
+ },
+ include: {
+ user: {
+ select: userFilterSelection
+ }
}
}))
From 1d212235a578938c5406dbdf38b11d548a024832 Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 20:20:28 +0100
Subject: [PATCH 3/8] chore: Add study programme admin page to the admin side
panel
---
src/app/admin/SlideSidebar.tsx | 4 ++++
src/app/admin/{studyprogrammes => study-programmes}/page.tsx | 0
.../studyProgrammeTable.module.scss | 0
.../studyProgrammeTable.tsx | 0
.../updateStudyProgrammeForm.tsx | 0
5 files changed, 4 insertions(+)
rename src/app/admin/{studyprogrammes => study-programmes}/page.tsx (100%)
rename src/app/admin/{studyprogrammes => study-programmes}/studyProgrammeTable.module.scss (100%)
rename src/app/admin/{studyprogrammes => study-programmes}/studyProgrammeTable.tsx (100%)
rename src/app/admin/{studyprogrammes => study-programmes}/updateStudyProgrammeForm.tsx (100%)
diff --git a/src/app/admin/SlideSidebar.tsx b/src/app/admin/SlideSidebar.tsx
index 6aa2f7e4a..24beae2ed 100644
--- a/src/app/admin/SlideSidebar.tsx
+++ b/src/app/admin/SlideSidebar.tsx
@@ -92,6 +92,10 @@ const navigations = [
{
title: 'Klasser',
href: '/admin/classes'
+ },
+ {
+ title: 'Studeprogrammer',
+ href: '/admin/study-programmes'
}
],
},
diff --git a/src/app/admin/studyprogrammes/page.tsx b/src/app/admin/study-programmes/page.tsx
similarity index 100%
rename from src/app/admin/studyprogrammes/page.tsx
rename to src/app/admin/study-programmes/page.tsx
diff --git a/src/app/admin/studyprogrammes/studyProgrammeTable.module.scss b/src/app/admin/study-programmes/studyProgrammeTable.module.scss
similarity index 100%
rename from src/app/admin/studyprogrammes/studyProgrammeTable.module.scss
rename to src/app/admin/study-programmes/studyProgrammeTable.module.scss
diff --git a/src/app/admin/studyprogrammes/studyProgrammeTable.tsx b/src/app/admin/study-programmes/studyProgrammeTable.tsx
similarity index 100%
rename from src/app/admin/studyprogrammes/studyProgrammeTable.tsx
rename to src/app/admin/study-programmes/studyProgrammeTable.tsx
diff --git a/src/app/admin/studyprogrammes/updateStudyProgrammeForm.tsx b/src/app/admin/study-programmes/updateStudyProgrammeForm.tsx
similarity index 100%
rename from src/app/admin/studyprogrammes/updateStudyProgrammeForm.tsx
rename to src/app/admin/study-programmes/updateStudyProgrammeForm.tsx
From 839cc40dae5f57b46f7a655e47767ecd4af60a38 Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 20:26:25 +0100
Subject: [PATCH 4/8] fix: build issue
---
src/app/admin/omegaid/container.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/app/admin/omegaid/container.tsx b/src/app/admin/omegaid/container.tsx
index 9db68409e..fd4fb8861 100644
--- a/src/app/admin/omegaid/container.tsx
+++ b/src/app/admin/omegaid/container.tsx
@@ -9,9 +9,9 @@ export default function OmegaIdContainer({
}) {
return ({
+ successCallback={async (userId) => ({
success: true,
- text: `${user.firstname} ${user.lastname}`,
+ text: `userID: ${userId}`,
})}
/>
}
From ede6c4b45f95b38e266f963c689f37b1c963e6a3 Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 20:30:06 +0100
Subject: [PATCH 5/8] fix: linting
---
src/app/_components/OmegaId/reader/OmegaIdReader.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
index d1ad5fe22..4e151fccc 100644
--- a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
+++ b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
@@ -5,7 +5,6 @@ import { parseJWT } from '@/jwt/parseJWTClient'
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'
/**
From 801b1e31acc77607df5e7ceee64d36d45ba535df Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 20:52:45 +0100
Subject: [PATCH 6/8] fix: resolve conversations
---
default.env | 2 ++
src/app/_components/OmegaId/reader/OmegaIdReader.tsx | 2 +-
src/jwt/jwt.ts | 10 +++++-----
src/services/omegaid/compress.ts | 2 +-
4 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/default.env b/default.env
index ada3d2c30..f7ff6660e 100644
--- a/default.env
+++ b/default.env
@@ -59,6 +59,8 @@ um/xyw/nKFJcqqMJ71Xq3SY+nA7I0ui4R4W6usx9He6kb5EKlzc9EdVq0w==
-----END PUBLIC KEY-----
"
+JWT_SECRET="hs_maa_gaa"
+
# Postfix
MAIL_DOMAIN=sanctus.omega.ntnu.no
MAIL_RELAY_HOST=mailgw.ntnu.no
diff --git a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
index 4e151fccc..5ba805b44 100644
--- a/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
+++ b/src/app/_components/OmegaId/reader/OmegaIdReader.tsx
@@ -2,10 +2,10 @@
import { QRCodeReaderConfig } from './ConfigVars'
import styles from './OmegaIdReader.module.scss'
import { parseJWT } from '@/jwt/parseJWTClient'
+import { decompressOmegaId } from '@/services/omegaid/compress'
import { Html5QrcodeScanner } from 'html5-qrcode'
import { useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'
-import { decomporessOmegaId as decompressOmegaId } from '@/services/omegaid/compress'
/**
* Renders a component for reading OmegaId QR codes.
diff --git a/src/jwt/jwt.ts b/src/jwt/jwt.ts
index d9eec9036..d62e9aacd 100644
--- a/src/jwt/jwt.ts
+++ b/src/jwt/jwt.ts
@@ -25,11 +25,11 @@ export function generateJWT(
expiresIn: number,
asymetric = false
): string {
- if (!process.env.NEXTAUTH_SECRET || !process.env.JWT_PRIVATE_KEY) {
+ if (!process.env.JWT_SECRET || !process.env.JWT_PRIVATE_KEY) {
throw new ServerError('INVALID CONFIGURATION', 'Missing secret for JWT generation')
}
- return sign(payload, Buffer.from(asymetric ? process.env.JWT_PRIVATE_KEY : process.env.NEXTAUTH_SECRET), {
+ return sign(payload, asymetric ? process.env.JWT_PRIVATE_KEY : process.env.JWT_SECRET, {
audience: aud,
algorithm: asymetric ? 'ES256' : 'HS256',
issuer: JWT_ISSUER,
@@ -44,16 +44,16 @@ export function generateJWT(
* @throws {ServerError} If the JWT is expired or invalid.
*/
export function verifyJWT(token: string, aud?: OmegaJWTAudience): (jwt.JwtPayload & Record) {
- if (!process.env.NEXTAUTH_SECRET || !process.env.JWT_PUBLIC_KEY) {
+ if (!process.env.JWT_SECRET || !process.env.JWT_PUBLIC_KEY) {
throw new ServerError(
'INVALID CONFIGURATION',
- 'JWT environ variables is not set. Missing NEXTAUTH_SECRET or JWT_PUBLIC_KEY'
+ 'JWT environ variables is not set. Missing JWT_SECRET or JWT_PUBLIC_KEY'
)
}
try {
const JWTHeader = readJWTPart(token, 0)
- let jwtKey = process.env.NEXTAUTH_SECRET
+ let jwtKey = process.env.JWT_SECRET
if (JWTHeader.alg === 'ES256') {
jwtKey = process.env.JWT_PUBLIC_KEY
}
diff --git a/src/services/omegaid/compress.ts b/src/services/omegaid/compress.ts
index 6f32b56fd..38a153ea7 100644
--- a/src/services/omegaid/compress.ts
+++ b/src/services/omegaid/compress.ts
@@ -73,7 +73,7 @@ function decompressPayload(rawdata: string): string {
return encodeBase64Url(payloadString)
}
-export function decomporessOmegaId(rawdata: string): ActionReturn {
+export function decompressOmegaId(rawdata: string): ActionReturn {
const header = {
alg: 'ES256',
typ: 'JWT'
From 57707389c392f73fe0fdd1b30269da9241fc2b8c Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 21:03:45 +0100
Subject: [PATCH 7/8] chore: Resolve conversations
---
src/app/admin/SlideSidebar.tsx | 2 +-
src/lib/jwt/jwt.ts | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/app/admin/SlideSidebar.tsx b/src/app/admin/SlideSidebar.tsx
index 24beae2ed..5fb61ac88 100644
--- a/src/app/admin/SlideSidebar.tsx
+++ b/src/app/admin/SlideSidebar.tsx
@@ -94,7 +94,7 @@ const navigations = [
href: '/admin/classes'
},
{
- title: 'Studeprogrammer',
+ title: 'Studieprogrammer',
href: '/admin/study-programmes'
}
],
diff --git a/src/lib/jwt/jwt.ts b/src/lib/jwt/jwt.ts
index d62e9aacd..f43ef0e42 100644
--- a/src/lib/jwt/jwt.ts
+++ b/src/lib/jwt/jwt.ts
@@ -17,6 +17,8 @@ export type JWT> = T & JwtPayloadType['Detailed']
* @param aud - An audience for the token, this is the purpose of the token
* @param payload - The payload to be included in the JWT.
* @param expiresIn - The expiration time of the JWT in seconds.
+ * @param asymetric - If this is set to true the JWT token will be signed with a private key,
+ * and can be verified with a public key. The public key is available for all users
* @returns The generated JWT.
*/
export function generateJWT(
From b70b9fa53d2b2b00b0f436eb2616f341443aa391 Mon Sep 17 00:00:00 2001
From: Theodor Kvalsvik Lauritzen
<19690242+theodorklauritzen@users.noreply.github.com>
Date: Thu, 7 Nov 2024 21:07:22 +0100
Subject: [PATCH 8/8] chore: Add JWT_SECRET to docker compose
---
docker-compose.dev.yml | 1 +
docker-compose.yml | 1 +
2 files changed, 2 insertions(+)
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 21dca214d..1e06d949c 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -37,6 +37,7 @@ services:
DOMAIN: ${DOMAIN}
JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY}
+ JWT_SECRET: ${JWT_SECRET}
depends_on:
db:
condition: service_healthy
diff --git a/docker-compose.yml b/docker-compose.yml
index c0fbd9bb9..a2c87caca 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -29,6 +29,7 @@ services:
DOMAIN: ${DOMAIN}
JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY}
+ JWT_SECRET: ${JWT_SECRET}
depends_on:
db:
condition: service_healthy