I’ve been trying to make the jwt creation and signing work in cf workers but unable to do so.
I’ve used these modules/libraries with node but no luck so far:
- @tsndr/cloudflare-worker-jwt - npm
- jose - npm
It keeps on throwing:
"TypeError: crypto2.createHmac is not a function"
This is my package.json
and I’m pretty sure that no other package is dependent on crypto/crypto2
:
{
"name": "portunus-worker",
"version": "1.0.0",
"description": "package for creating workers templates",
"main": "index.js",
"scripts": {
"test": "jest",
"format": "prettier --write '**/*.{js,css,jsone,md}'",
"lint": "eslint *.js modules/*.js",
"dev": "npx wrangler dev"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@cloudflare/wrangler": "^1.19.5",
"eslint": "^8.5.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.4.5",
"prettier": "^2.2.1"
},
"dependencies": {
"@tsndr/cloudflare-worker-jwt": "^2.2.1",
"deta-worker": "^0.2.0-alpha",
"eslint-plugin-jest": "^25.3.4",
"itty-router": "^2.5.2",
"itty-router-extras": "^0.4.2",
"jose": "^4.14.4",
"otplib": "^12.0.1",
"serverless-cloudflare-workers": "^1.2.0",
"uuid": "^8.3.2"
}
}
Not sure what I’m missing, any help is appreciated, thanks.
Can you share the code for your worker @ashishjullia19? I have used @tsndr/cloudflare-worker-jwt - npm
without issue before.
@anon9246926
Here is the code: GitHub - portunus-dev/portunus-worker: edge workers as API servers
Wherever there are refs to jwt.
, I just switched the jsonwebtoken
to this Library.
Did you await
this per this example
const token = await jwt.sign({ name: 'John Doe', email: '[email protected]' }, 'secret')
Not sure where this is coming from as I don’t believe either package relies on this module.
@anon9246926
Yes, I did use await
.
I’m not sure either from where this crypto2 error is coming from.
Seems like the problem is with otplib
library, not sure how to solve this though as I’m unable to find a library which does no rely on crypto
:
TypeError: crypto2.createHmac is not a function
at Object.createDigest (core:user:portunus-worker-dev:2616:30)
at hotpDigest (core:user:portunus-worker-dev:2991:24)
at hotpToken (core:user:portunus-worker-dev:2994:45)
at totpToken (core:user:portunus-worker-dev:3107:16)
at authenticatorToken (core:user:portunus-worker-dev:3254:16)
at Authenticator.generate (core:user:portunus-worker-dev:3267:18)
at module.exports.getOTP (core:user:portunus-worker-dev:5109:38)
at async Object.handle (core:user:portunus-worker-dev:2186:37)
at async jsonError (core:user:portunus-worker-dev:2149:18) {
stack: TypeError: crypto2.createHmac is not a function
…jsonError (core:user:portunus-worker-dev:2149:18),
message: crypto2.createHmac is not a function
}
I’m trying to use otplib
library in cloudflare workers but seems like it is not supported because of a crypto/crypto2 dependency, at least that’s what I understood from this error:
Error generating OTP: crypto2.createHmac is not a function
TypeError: crypto2.createHmac is not a function
at Object.createDigest (core:user:portunus-worker-dev:2616:30)
at hotpDigest (core:user:portunus-worker-dev:2991:24)
at hotpToken (core:user:portunus-worker-dev:2994:45)
at totpToken (core:user:portunus-worker-dev:3107:16)
at authenticatorToken (core:user:portunus-worker-dev:3254:16)
at Authenticator.generate (core:user:portunus-worker-dev:3267:18)
at module.exports.getOTP (core:user:portunus-worker-dev:5109:38)
at async Object.handle (core:user:portunus-worker-dev:2186:37)
at async jsonError (core:user:portunus-worker-dev:2149:18) {
stack: TypeError: crypto2.createHmac is not a function
…jsonError (core:user:portunus-worker-dev:2149:18),
message: crypto2.createHmac is not a function
}
The code:
// Auth handlers
module.exports.getOTP = async ({ query, url, headers, cf = {} }) => {
const { user, origin } = query // user is email
if (!user || !EMAIL_REGEXP.test(user)) {
return respondError(new HTTPError('User email not supplied', 400))
}
let u = await fetchUser(user)
console.log(u.otp_secret)
console.log(u)
if (!u) {
u = await createUser(user)
} else if (!u.otp_secret) {
// legacy user without otp_secret
u.otp_secret = uuidv4()
if (u.otp_secret === u.jwt_uuid) {
// Note: this shouldn't happen anyway
throw new Error('OTP secret and JWT UUID are the same')
}
u.updated = new Date()
await updateUser(u)
}
let otp;
try {
otp = totp.generate(u.otp_secret);
} catch (err) {
console.error("Error generating OTP:", err.message);
throw err; // you can choose to rethrow the error, or handle it here and not throw
}
const expiresAt = new Date(Date.now() + totp.timeRemaining() * 1000)
// TODO: tricky for local dev as the origin is mapped to remote cloudflare worker
const { origin: _origin } = new URL(url)
const defaultOrigin = `${_origin}/login`
// obtain locale and timezone from request for email `expiresAt` formatting
const locale = (headers.get('Accept-Language') || '').split(',')[0] || 'en'
const timeZone = cf.timezone || 'UTC'
const plainEmail = [
`OTP: ${otp}`,
`Magic-Link: ${origin || defaultOrigin}?user=${user}&otp=${otp}`,
`Expires at: ${expiresAt.toLocaleString(locale, {
timeZone,
timeZoneName: 'long',
})}`,
].join('\n')
await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
authorization: `Bearer ${MAIL_PASS}`,
'content-type': 'application/json',
},
body: JSON.stringify({
personalizations: [{ to: [{ email: u.email }] }],
from: { email: '[email protected]' },
subject: 'OTP',
content: [
{
type: 'text/plain',
value: plainEmail,
},
],
}),
})
return respondJSON({ payload: { message: `OTP/Magic-Link sent to ${user}` } })
}
I tried looking for a supported library for the same task but unable to find one.
Any help is appreciated.