Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/app/(auth)/login/page.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,19 @@
color: ohma.$colors-gray-500;
}
}

.loginContainer {
max-width: 200px;

* {
width: 100%;
}

button, input, p {
margin: ohma.$gap 0;
}

p {
font-size: ohma.$fonts-s;
}
}
15 changes: 8 additions & 7 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ export default function LogIn() {
})
}

return <>
return <div className={styles.loginContainer}>
<form onSubmit={handleSignIn}>
<TextInput label="Brukernavn" name="username" type="text"/>
<TextInput label="Passord" name="password" type="password"/>
<BorderButton>Logg inn</BorderButton>
<p style={{ color: 'red' }}>{error === 'CredentialsSignin' ? 'Feil brukernavn eller passord :(' : ''}</p>
<p><Link href="/send-reset-password-email" className={styles.resetPasswordLink}>Glemt passord?</Link></p>
<p style={{ color: 'red', visibility: error === 'CredentialsSignin' ? 'visible' : 'hidden' }}>
{'Feil brukernavn eller passord. :('}
</p>
</form>
<BorderButton onClick={() => signIn('feide', {
redirect: true,
callbackUrl: searchParams.get('callbackUrl') || '/users/me'
})}>Logg inn med Feide</BorderButton>
Comment thread
Paulijuz marked this conversation as resolved.
<br />
<Link href="/send-reset-password-email" className={styles.resetPasswordLink}>Glemt passord?</Link>
<p>Er det første gang du logger inn? Da er det bare å logge inn med feide for å lage en bruker.</p>
</>
})}>Fortsett med Feide</BorderButton>
<p>For å opprette bruker logg inn med Feide.</p>
</div>
}
12 changes: 8 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@ import { readUserProfileAction } from '@/services/users/actions'
import { unwrapActionReturn } from './redirectToErrorPage'
import { frontpageAuth } from '@/services/frontpage/auth'
import { ServerSession } from '@/auth/session/ServerSession'
import type { Metadata } from 'next'

config.autoAddCss = false

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
title: 'projectnext',
description: '',
charset: 'utf-8',
export const metadata: Metadata = {
title: {
default: 'Sct. Omega Broderskab',
template: '%s | Sct. Omega Broderskab',
},
description: 'Hjemmesiden for linjeforeningen Sanctus Omega Broderskab ved NTNU.',
keywords: ['Sanctus Omega Broderskab', 'Sct. Omega Broderskab', 'Sanctus Omega', 'Sct. Omega', 'Omega'],
}

type PropTypes = {
Expand Down
5 changes: 5 additions & 0 deletions src/app/users/[username]/(user-admin)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { notFound } from 'next/navigation'
import Link from 'next/link'
import type { ReactNode } from 'react'
import type { PropTypes } from '@/app/users/[username]/page'
import type { Metadata } from 'next'

export const metadata: Metadata = {
title: 'Innstillinger',
}

export default async function UserAdmin({ children, params }: PropTypes & { children: ReactNode }) {
const session = await ServerSession.fromNextAuth()
Expand Down
4 changes: 4 additions & 0 deletions src/app/users/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import Link from 'next/link'
import { notFound, redirect } from 'next/navigation'
import { v4 as uuid } from 'uuid'
import React from 'react'
import type { Metadata } from 'next'

export const metadata: Metadata = {
title: 'Profil',
}

export type PropTypes = {
params: Promise<{
Expand Down
29 changes: 27 additions & 2 deletions src/auth/nextAuth/authOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { updateEmailForFeideAccount } from '@/services/auth/feideAccounts/update
import { userOperations } from '@/services/users/operations'
import { permissionOperations } from '@/services/permissions/operations'
import CredentialsProvider from 'next-auth/providers/credentials'
import { decode } from 'next-auth/jwt'
import { encode, decode } from 'next-auth/jwt'
import type { AuthOptions } from 'next-auth'
import { compressJwt, decompressJwt } from './jwtCompression'

export const authOptions: AuthOptions = {
providers: [
Expand Down Expand Up @@ -54,8 +55,17 @@ export const authOptions: AuthOptions = {
strategy: 'jwt'
},
jwt: {
async encode(params) {
params.token = await compressJwt(params.token)
return encode(params)
},

async decode(params) {
const token = await decode(params)
const decodedToken = await decode(params)

if (!decodedToken) return null

const token = await decompressJwt(decodedToken)

// iat = issued at (timestamp given in seconds since epoch)
if (!token || !token.iat) return null
Expand Down Expand Up @@ -184,4 +194,19 @@ export const authOptions: AuthOptions = {
newUser: '/register',
},
adapter: VevenAdapter(prisma),
logger: {
error(code, metadata) {
// When in development mode JWT are invalidated at each restart,
// thus producing a lot of noise in both the logs and in the browser.
// Therefore, we log these as warnings instead of errors.
const logFunction = code === 'JWT_SESSION_ERROR' ? console.warn : console.error
logFunction(`NextAuth error: ${code}`, metadata)
},
warn(code) {
console.warn(`NextAuth warning: ${code}`)
},
debug(code, metadata) {
console.debug(`NextAuth debug: ${code}`, metadata)
},
}
}
71 changes: 71 additions & 0 deletions src/auth/nextAuth/jwtCompression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { deflate, inflate } from 'zlib'
import { promisify } from 'util'
import type { JWT } from 'next-auth/jwt'

// THIS WACKY COMPRESSING AND DECOMPRESSING STUFF IS TEMPORARY
// TO REDUCE JWT SIZE UNTIL WE HAVE EITHER:
// 1) MORE REASONABLE PERMISSIONS ENCODING
// 2) SWITCHED TO DATABASE SESSIONS
// 3) FIX THE JWT TYPE TO SUPPORT COMPRESSION PROPERLY

const deflateAsync = promisify(deflate)
const inflateAsync = promisify(inflate)

// eslint-disable-next-line
async function compressField(data: any): Promise<any> {
try {
const buffer = await deflateAsync(JSON.stringify(data))
return buffer.toString('base64')
} catch (error) {
console.error('Failed to compress JWT field: ', error)

return null
}
}

// eslint-disable-next-line
async function decompressField(data: any): Promise<any> {
try {
const buffer = Buffer.from(data, 'base64')
const inflated = await inflateAsync(buffer)
return JSON.parse(inflated.toString())
} catch (error) {
console.error('Failed to decompress JWT field: ', error)

return null
}
}

const fieldsToCompress: (keyof JWT)[] = ['permissions', 'memberships', 'user']

export async function compressJwt(token: JWT | undefined): Promise<JWT | undefined> {
if (!token) return token

const compressedJwt = { ...token }

for (const field of fieldsToCompress) {
if (field in compressedJwt) {
compressedJwt[field] = await compressField(compressedJwt[field])
}
}

return compressedJwt
}

export async function decompressJwt(token: JWT): Promise<JWT | null> {
const decompressedJwt = { ...token }

for (const field of fieldsToCompress) {
if (field in decompressedJwt) {
const decompressedField = await decompressField(decompressedJwt[field])

if (decompressedField === null) {
return null
}

decompressedJwt[field] = decompressedField
}
}

return decompressedJwt
}
Loading