Jose DataError: HMAC key data must not be empty

Hey y'all. Working on adding my own auth flow to my Next/T3 app using a magic link/email. The link takes you to /api/sign/<your auth token> where Jose signs a JWT and I attach it to a cookie. It had been working fine before, but suddenly it's throwing a DataError: HMAC key data must not be empty error. I did some Googling, came across only one instance where someone had this issue on GitHub (https://github.com/panva/jose/issues/698) but wasn't helping at all. Using the following package versions: - next 14.2.1 - jose 5.9.6
GitHub
HMAC key data must not be empty · Issue #698 · panva/jose
What happened? I am trying to use compactVerify (just to verify signature is matching my HS256 key, nothing else), but when sending in an empty string, as the secret parameter (after encoding it in...
5 Replies
caleb
calebOP3d ago
Here's the /src/app/api/sign/route.ts file:
import { NextResponse } from 'next/server'
import { api } from '~/trpc/server'
import * as jose from 'jose'
import { env } from '~/env'

export async function GET(req: Request, { params }: { params: { token: string } }) {
const token = params.token

const { message } = await api.authentication.verifyUserAuthenticationTokenCode({ token })

if (message === 'not_found') {
return NextResponse.redirect(new URL('/signin?message=code_not_found', req.url))
} else if (message === 'expired') {
return NextResponse.redirect(new URL('/signin?message=code_expired', req.url))
} else if (message === 'verified') {
const secret = new TextEncoder().encode(env.JWT_SECRET)

const user = await api.user.getUserByAuthToken({ token })

if (!user) {
return NextResponse.redirect(new URL('/signin?message=user_not_found', req.url))
}

const jwt = await new jose.SignJWT().setProtectedHeader({ alg: 'HS256' }).setIssuedAt().setIssuer('urn:caaampus:issuer').setAudience('urn:caaampus:audience').setSubject(JSON.stringify(user.id)).setExpirationTime('72h').sign(secret)

const response = NextResponse.redirect(new URL('/profile', req.url))
response.cookies.set('Authorization', jwt, {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
expires: new Date(Date.now() + 72 * 60 * 60 * 1000)
})

return response
}
}
import { NextResponse } from 'next/server'
import { api } from '~/trpc/server'
import * as jose from 'jose'
import { env } from '~/env'

export async function GET(req: Request, { params }: { params: { token: string } }) {
const token = params.token

const { message } = await api.authentication.verifyUserAuthenticationTokenCode({ token })

if (message === 'not_found') {
return NextResponse.redirect(new URL('/signin?message=code_not_found', req.url))
} else if (message === 'expired') {
return NextResponse.redirect(new URL('/signin?message=code_expired', req.url))
} else if (message === 'verified') {
const secret = new TextEncoder().encode(env.JWT_SECRET)

const user = await api.user.getUserByAuthToken({ token })

if (!user) {
return NextResponse.redirect(new URL('/signin?message=user_not_found', req.url))
}

const jwt = await new jose.SignJWT().setProtectedHeader({ alg: 'HS256' }).setIssuedAt().setIssuer('urn:caaampus:issuer').setAudience('urn:caaampus:audience').setSubject(JSON.stringify(user.id)).setExpirationTime('72h').sign(secret)

const response = NextResponse.redirect(new URL('/profile', req.url))
response.cookies.set('Authorization', jwt, {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
expires: new Date(Date.now() + 72 * 60 * 60 * 1000)
})

return response
}
}
caleb
calebOP3d ago
I was using middleware to parse and validate the auth cookie but needed to use Prisma. I had issues adding Prisma to edge runtime so I instead moved it over to my providers.tsx file. Discord isn't letting me post the code for some reason so here's a Hastebin link with the providers code: https://hastebin.com/share/enomewaken.javascript
Hastebin
Hastebin is a free web-based pastebin service for storing and sharing text and code snippets with anyone. Get started now.
caleb
calebOP3d ago
I verified that jwt and secret in the providers code are not null or blank, which is what it looks like the error I'm getting would be caused by. I also confirmed they're the exact same from when the JWT is being signed.
Reptiloid
Reptiloid3d ago
It seems like the secret variable value comes from the env.JWT_SECRET. Maybe it's not set?
caleb
calebOP3d ago
I made sure jwt and secret are both set. I even logged it to make sure

Did you find this page helpful?