payload-auth-plugin
Version:
Authentication plugin for Payload CMS
75 lines (71 loc) • 1.73 kB
text/typescript
import * as jose from "jose"
export function hashCode(s: string) {
let h = 0
const l = s.length
let i = 0
if (l > 0) while (i < l) h = ((h << 5) + h + s.charCodeAt(i++)) | 0
return h
}
export const ephemeralCode = async (length: number, secret: string) => {
const code: number[] = []
while (code.length < length) {
const buffer = crypto.getRandomValues(new Uint8Array(length * 2))
for (const byte of buffer) {
if (byte < 250 && code.length < length) {
code.push(byte % 10)
}
}
}
const codeStr = code.join("")
const iterations = 600000
const encoder = new TextEncoder()
const bytes = encoder.encode(codeStr)
const salt = encoder.encode(secret)
const keyMaterial = await crypto.subtle.importKey(
"raw",
bytes,
"PBKDF2",
false,
["deriveBits"],
)
const hash = await crypto.subtle.deriveBits(
{
name: "PBKDF2",
hash: "SHA-256",
salt: salt,
iterations,
},
keyMaterial,
256,
)
const hashB64 = jose.base64url.encode(new Uint8Array(hash))
return {
hash: hashB64,
code: codeStr,
}
}
export const verifyEphemeralCode = async (
code: string,
hashB64: string,
secret: string,
) => {
const encoder = new TextEncoder()
const codeBytes = encoder.encode(code)
const salt = encoder.encode(secret)
const params = {
name: "PBKDF2",
hash: "SHA-256",
salt,
iterations: 600000,
}
const keyMaterial = await crypto.subtle.importKey(
"raw",
codeBytes,
"PBKDF2",
false,
["deriveBits"],
)
const hash = await crypto.subtle.deriveBits(params, keyMaterial, 256)
const hashBase64 = jose.base64url.encode(new Uint8Array(hash))
return hashBase64 === hashB64
}