Socket
Socket
Sign inDemoInstall

@1auth/crypto

Package Overview
Dependencies
Maintainers
0
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@1auth/crypto - npm Package Compare versions

Comparing version 0.0.0-alpha.32 to 0.0.0-alpha.33

519

index.js

@@ -8,4 +8,4 @@ import { promisify } from 'node:util'

generateKeyPair as generateKeyPairCallback,
sign,
verify
sign as signCallback,
verify as verifyCallback
} from 'node:crypto'

@@ -15,17 +15,33 @@ // https://github.com/napi-rs/node-rs/tree/main/packages/argon2

const options = {
// randomBytes(32).toString('hex') // 256 bits
encryptionContextKey: null,
encryptionSharedKey: '',
encryptionMethod: 'chacha20-poly1305', // AES-256 GCM (aes-256-gcm) or ChaCha20-Poly1305 (chacha20-poly1305)
const generateKeyPair = promisify(generateKeyPairCallback)
const sign = promisify(signCallback)
const verify = promisify(verifyCallback)
const defaults = {
// symetricEncryptionKey: randomBytes(32).toString('base64') // 256 bits
symetricEncryptionKey: undefined,
symetricEncryptionMethod: 'chacha20-poly1305', // 2024-05: AES-256 GCM (aes-256-gcm) or ChaCha20-Poly1305 (chacha20-poly1305)
symetricEncryptionEncoding: 'base64', // https://nodejs.org/api/buffer.html#buffers-and-character-encodings
asymetricKey: 'P-384',
digestAlgorithm: 'sha3-384',
digestSalt: ''
signatureEncoding: 'base64'
}
export default (params) => {
Object.assign(options, params)
const symetricEncryptionEncodingLengths = {}
const options = {}
export default (opt = {}) => {
Object.assign(options, defaults, opt)
symetricEncryptionEncodingLengths.iv = randomBytes(12).toString(
options.symetricEncryptionEncoding
).length
symetricEncryptionEncodingLengths.ivAndAuthTag =
symetricEncryptionEncodingLengths.iv +
randomBytes(16).toString(options.symetricEncryptionEncoding).length
if (!options.symetricEncryptionKey) {
console.warn(
"@1auth/crypto symetricEncryptionKey is empty, use a stored secret made from randomBytes(32).toString('base64'). Encryption disabled."
)
}
}
const generateKeyPair = promisify(generateKeyPairCallback)
export const getOptions = () => options

@@ -48,186 +64,105 @@ // *** entropy *** //

// *** Random generators *** //
export const characterPoolSize = {
keyboard: 94, // (26 + 10 + 11) * 2
alphaNumeric: 62, // (26 + 10) * 2
base64: 64, // (26 * 2 + 10 + 2
hex: 16,
numeric: 10
// *** Helpers *** //
// Ref: https://therootcompany.com/blog/how-many-bits-of-entropy-per-character/
export const entropyToCharacterLength = (bits, characterPoolSize) => {
// bits*ln(2)/ln(characterPoolSize)
return Math.ceil((bits * Math.LN2) / Math.log(characterPoolSize))
}
// Alt: https://github.com/sindresorhus/crypto-random-string/blob/main/core.js
export const randomAlphaNumeric = (bits) => {
const characterLength = entropyToCharacterLength(
bits,
characterPoolSize.alphaNumeric
)
return randomBytes(characterLength * 2)
.toString('base64')
.replace(/[^a-zA-Z0-9]/g, '')
.substring(0, characterLength)
}
/* export const characterLengthToEntropy = (
characterLength,
characterPoolSize,
) => {
// log_2(characterPoolSize^characterLength)
return Math.floor(Math.log2(characterPoolSize ** characterLength));
}; */
export const randomBase64 = (bits) => {
const characterLength = entropyToCharacterLength(
bits,
characterPoolSize.base64
)
return randomBytes(characterLength + 1)
.toString('base64')
.replace('=', '')
}
// *** Random generators *** //
// Ref: https://github.com/sindresorhus/crypto-random-string/blob/main/core.js
export const charactersAlphaNumeric = [
...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
]
export const charactersDistinguishable = [...'CDEHKMPRTUWXY012458']
export const charactersNumeric = [...'0123456789']
export const randomNumeric = (bits) => {
const characterLength = entropyToCharacterLength(
bits,
characterPoolSize.numeric
)
const max = 10 ** characterLength
let value = Math.floor(Math.random() * max).toString()
if (value === max.toString()) {
return randomNumeric(bits)
}
while (value.length < characterLength) value = '0' + value
return value
}
export const randomCharacters = (
length,
characters = charactersAlphaNumeric
) => {
// Generating entropy is faster than complex math operations, so we use the simplest way
const characterCount = characters.length
const maxValidSelector =
Math.floor(0x1_00_00 / characterCount) * characterCount - 1 // Using values above this will ruin distribution when using modular division
const entropyLength = 2 * Math.ceil(1.1 * length) // Generating a bit more than required so chances we need more than one pass will be really low
let string = ''
let stringLength = 0
export const randomHex = (bits) => {
const characterLength = entropyToCharacterLength(bits, characterPoolSize.hex)
return randomBytes(characterLength).toString('hex')
}
while (stringLength < length) {
// In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it
const entropy = new Uint8Array(randomBytes(entropyLength))
let entropyPosition = 0
// *** configs *** //
export const randomId = {
type: 'id',
entropy: 64,
charPool: characterPoolSize.alphaNumeric,
// TODO update to use https://github.com/jetpack-io/typeid
create: async (prefix) =>
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.entropy)
}
while (entropyPosition < entropyLength && stringLength < length) {
const entropyValue =
entropy[entropyPosition] + (entropy[entropyPosition + 1] << 8) // eslint-disable-line no-bitwise
entropyPosition += 2
if (entropyValue > maxValidSelector) {
// Skip values which will ruin distribution when using modular division
continue
}
export const subject = {
type: 'id',
entropy: 64,
charPool: characterPoolSize.alphaNumeric,
create: async (prefix) =>
(prefix ? prefix + '_' : '') + randomAlphaNumeric(subject.entropy)
}
string += characters[entropyValue % characterCount]
stringLength++
}
}
export const session = {
type: 'id',
entropy: 128, // ASVS 3.2.2
charPool: characterPoolSize.alphaNumeric,
expire: 15 * 60,
create: async (prefix) =>
(prefix ? prefix + '_' : '') + randomAlphaNumeric(session.entropy)
return string
}
export const passwordSecret = {
type: 'secret',
entropy: 64,
charPool: characterPoolSize.keyboard,
otp: false,
encode: async (value, encryptedKey, sub) =>
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
verify: async (value, hash) => verifySecretHash(hash, value)
export const randomAlphaNumeric = (characterLength) => {
return randomCharacters(characterLength, charactersAlphaNumeric)
}
// aka Password/Credential Recovery
export const passwordToken = {
type: 'token',
entropy: 20,
charPool: characterPoolSize.numeric,
otp: true,
expire: 30,
create: async () => randomNumeric(passwordToken.entropy),
encode: async (value, encryptedKey, sub) =>
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
verify: async (value, hash) => verifySecretHash(hash, value)
export const randomNumeric = (characterLength) => {
return randomCharacters(characterLength, charactersNumeric)
}
export const oneTimeSecret = {
type: 'secret',
entropy: 64, //
charPool: characterPoolSize.base64,
otp: false,
expire: null,
create: async () => randomBase64(oneTimeSecret.entropy),
encode: async (value, encryptedKey, sub) => encrypt(value, encryptedKey, sub),
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub)
// create, hash, verify to be handled within
export const randomSymetricEncryptionKey = () => {
return randomBytes(32).toString('base64') // 256 bits
}
export const oneTimeToken = {
type: 'token',
entropy: 20,
charPool: characterPoolSize.numeric,
otp: true,
expire: 30
// create, hash, verify to be handled within
}
// aka lookup secret
export const recoveryCode = {
type: 'secret',
entropy: 112,
charPool: characterPoolSize.alphaNumeric,
otp: true,
create: async () => randomAlphaNumeric(recoveryCode.entropy),
encode: async (value, encryptedKey, sub) =>
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
verify: async (value, hash) => verifySecretHash(hash, value)
// *** configs *** //
export const randomId = {
type: 'id',
minLength: entropyToCharacterLength(64, charactersAlphaNumeric.length),
// TODO update to use https://github.com/jetpack-io/typeid
create: (prefix) =>
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.minLength)
}
export const outOfBandToken = {
type: 'token',
entropy: 20,
charPool: characterPoolSize.numeric,
otp: true,
expire: 10 * 60,
create: async () => randomNumeric(outOfBandToken.entropy),
encode: async (value, encryptedKey, sub) =>
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
verify: async (value, hash) => verifySecretHash(hash, value)
// *** Digests *** //
export const createDigest = (value, { algorithm } = {}) => {
algorithm ??= options.digestAlgorithm
const hash = checksum(algorithm).update(value).digest('hex')
const digest = `${algorithm}:${hash}`
return digest
}
export const accessToken = {
type: 'secret',
entropy: 112,
charPool: characterPoolSize.alphaNumeric,
otp: false,
expire: 30 * 24 * 60 * 60, // allow override from user
create: async () => randomAlphaNumeric(accessToken.entropy),
encode: async (value, encryptedKey, sub) =>
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
verify: async (value, hash) => verifySecretHash(hash, value)
export const createEncryptedDigest = (value, { algorithm } = {}) => {
const digest = createDigest(value, { algorithm })
// encrypting using the symetricEncryptionKey instread of
// the row encryptionKey to allow lookup, must have fixed iv
return symetricEncrypt(digest, {
encryptionKey: options.symetricEncryptionKey,
sub: '',
encoding: options.symetricEncryptionEncoding,
iv: Buffer.from(
options.symetricEncryptionKey.substring(
0,
symetricEncryptionEncodingLengths.iv
),
options.symetricEncryptionEncoding
)
})
}
// *** Helpers *** //
export const characterLengthToEntropy = (
characterLength,
characterPoolSize
) => {
// log_2(characterPoolSize^characterLength)
return Math.round(Math.log2(characterPoolSize ** characterLength))
}
export const entropyToCharacterLength = (bits, characterPoolSize) => {
// bits*ln(2)/ln(characterPoolSize)
return Math.round((bits * Math.LN2) / Math.log(characterPoolSize))
}
// *** Digests *** //
export const createDigest = async (value, { algorithm, salt } = {}) => {
algorithm ??= options.digestAlgorithm
salt ??= options.digestSalt
const hash = checksum(algorithm)
.update(value + salt)
.digest('hex')
return `${algorithm}:${hash}`
}
// *** Hashing *** //

@@ -255,65 +190,88 @@ const hashOptions = {

export const makeSymetricKey = (assocData) => {
if (!options.encryptionSharedKey) {
export const makeSymetricKey = (sub) => {
if (!options.symetricEncryptionKey) {
return { encryptionKey: '', encryptedKey: '' }
}
const encryptionKey = randomBytes(32)
const encryptedKey = __encrypt(
encryptionKey,
Buffer.from(options.encryptionSharedKey, 'hex'),
assocData,
'hex',
'hex'
)
const encryptionKey = randomSymetricEncryptionKey()
const encryptedKey = symetricEncrypt(encryptionKey, {
encryptionKey: options.symetricEncryptionKey,
sub,
decoding: 'base64',
encoding: options.symetricEncryptionEncoding
})
return { encryptionKey, encryptedKey }
}
// assocData = sub or id
export const encryptFields = (values, encryptedKey, assocData, fields = []) => {
// TODO optimize: don't decrypt encryptedKey more than once
// sub add context to encryption
export const symetricEncryptFields = (
values,
{ encryptedKey, encryptionKey, sub },
fields = []
) => {
if (encryptedKey) {
encryptionKey ??= symetricDecryptKey(encryptedKey, {
sub
})
}
if (!encryptionKey) return values
const encryptedValues = structuredClone(values)
for (const key of fields) {
values[key] &&= encrypt(values[key], encryptedKey, assocData)
encryptedValues[key] &&= symetricEncrypt(encryptedValues[key], {
encryptionKey,
sub
})
}
return values
return encryptedValues
}
export const encrypt = (data, encryptedKey, assocData) => {
if (!encryptedKey) return data
export const symetricEncrypt = (
data,
{ encryptedKey, encryptionKey, sub, decoding, encoding, iv }
) => {
if (encryptedKey) {
encryptionKey ??= symetricDecryptKey(encryptedKey, {
sub
})
}
if (!encryptionKey) return data
decoding ??= 'utf8'
encoding ??= options.symetricEncryptionEncoding
iv ??= randomBytes(12) // 96 bits
const encryptionKey = __decryptKey(encryptedKey, assocData)
return __encrypt(
data,
Buffer.from(encryptionKey, 'hex'),
assocData,
'utf8',
'hex'
const cipher = createCipheriv(
options.symetricEncryptionMethod,
Buffer.from(encryptionKey, 'base64'),
iv,
{
authTagLength
}
)
}
const __encrypt = (
data,
encryptionKey,
assocData,
decoding = 'utf8',
encoding = 'hex'
) => {
const iv = randomBytes(12) // 96 bits
const cipher = createCipheriv(options.encryptionMethod, encryptionKey, iv, {
authTagLength
})
cipher.setAAD(Buffer.from(assocData, 'utf8'))
cipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
const encryptedData =
cipher.update(data, decoding, encoding) + cipher.final(encoding)
const authTag = cipher.getAuthTag()
return (
iv.toString(encoding) + // 24 char
authTag.toString(encoding) + // 32 char
encryptedData
)
const encryptedDataPacket =
iv.toString(encoding) + authTag.toString(encoding) + encryptedData
return encryptedDataPacket
}
export const decryptFields = (values, encryptedKey, assocData, fields = []) => {
// TODO optimize: don't decrypt encryptedKey more than once
export const symetricDecryptFields = (
encryptedValues,
{ encryptedKey, encryptionKey, sub },
fields = []
) => {
if (encryptedKey) {
encryptionKey ??= symetricDecryptKey(encryptedKey, {
sub
})
}
if (!encryptionKey) return encryptedValues
const values = structuredClone(encryptedValues)
for (const key of fields) {
values[key] &&= decrypt(values[key], encryptedKey, assocData)
values[key] &&= symetricDecrypt(values[key], {
encryptionKey,
sub
})
}

@@ -323,38 +281,45 @@ return values

export const decrypt = (encryptedData, encryptedKey, assocData) => {
if (!options.encryptionSharedKey || !encryptedKey) return encryptedData
const encryptionKey = __decryptKey(encryptedKey, assocData)
const data = __decrypt(
encryptedData,
Buffer.from(encryptionKey, 'hex'),
assocData,
'hex',
'utf8'
)
return data
export const symetricDecryptKey = (encryptedKey, { sub } = {}) => {
return symetricDecrypt(encryptedKey, {
encryptionKey: options.symetricEncryptionKey,
sub,
decoding: options.symetricEncryptionEncoding,
encoding: 'base64'
})
}
const __decryptKey = (encryptedKey, assocData) =>
__decrypt(
encryptedKey,
Buffer.from(options.encryptionSharedKey, 'hex'),
assocData,
'hex',
'hex'
export const symetricDecrypt = (
encryptedDataPacket,
{ encryptedKey, encryptionKey, sub, decoding, encoding }
) => {
if (encryptedKey) {
encryptionKey ??= symetricDecryptKey(encryptedKey, {
sub
})
}
if (!encryptionKey) return encryptedDataPacket
decoding ??= options.symetricEncryptionEncoding
encoding ??= 'utf8'
const iv = Buffer.from(
encryptedDataPacket.substring(0, symetricEncryptionEncodingLengths.iv),
decoding
)
const authTag = Buffer.from(
encryptedDataPacket.substring(
symetricEncryptionEncodingLengths.iv,
symetricEncryptionEncodingLengths.ivAndAuthTag
),
decoding
)
const encryptedData = Buffer.from(
encryptedDataPacket.substring(
symetricEncryptionEncodingLengths.ivAndAuthTag
),
decoding
)
const __decrypt = (
data,
encryptionKey,
assocData,
decoding = 'hex',
encoding = 'utf8'
) => {
const iv = Buffer.from(data.substring(0, 24), decoding)
const authTag = Buffer.from(data.substring(24, 56), decoding)
const encryptedData = Buffer.from(data.substring(56), decoding)
const decipher = createDecipheriv(
options.encryptionMethod,
encryptionKey,
options.symetricEncryptionMethod,
Buffer.from(encryptionKey, 'base64'),
iv,

@@ -365,12 +330,14 @@ {

)
decipher.setAAD(Buffer.from(assocData, 'utf8'))
decipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
decipher.setAuthTag(authTag)
return (
const data =
decipher.update(encryptedData, decoding, encoding) +
decipher.final(encoding)
)
return data
}
// *** Signatures *** //
export const makeAsymmetricKeys = async (encryptionKey) => {
// asymmetricKeyPairType
export const makeAsymmetricKeys = async () => {
const { publicKey, privateKey } = await generateKeyPair('ec', {

@@ -385,6 +352,6 @@ namedCurve: 'P-384', // P-512

type: 'sec1',
format: 'pem',
// TODO remove encryption for other with sub check?
cipher: options.encryptionMethod,
passphrase: encryptionKey
format: 'pem'
// Encryption done at another level for consistency
// cipher: options.asymetricEncryptionMethod,
// passphrase: encryptionKey,
}

@@ -395,33 +362,21 @@ })

export const makeSignature = (data, privateKey, algorithm = 'SHA3-384') => {
return sign(algorithm, Buffer.from(data), privateKey).toString('base64')
export const makeSignature = async (data, privateKey, { algorithm } = {}) => {
algorithm ??= options.digestAlgorithm
return (await sign(algorithm, Buffer.from(data), privateKey)).toString(
options.signatureEncoding
)
}
export const verifySignature = (
export const verifySignature = async (
data,
publicKey,
signature,
algorithm = 'SHA3-384'
{ algorithm } = {}
) => {
return verify(
algorithm ??= options.digestAlgorithm
return await verify(
algorithm,
Buffer.from(data),
publicKey,
Buffer.from(signature, 'base64')
Buffer.from(signature, options.signatureEncoding)
)
}
// const assocData = 'sub'
// const input = 'data'
//
// const {encryptedKey} = makeSymetricKey(assocData)
// const encryptedData = encrypt(input, encryptedKey, assocData)
// const output = decrypt(encryptedData, encryptedKey, assocData)
// console.log(assocData, input, output, encryptedData)
// const assocData = 'tlSIcVp1XEF'
// const encryptionKey =
// 'b9ebfdab6603dd8e151138ec8298aaf8b49bf6d38480f9864e9e72e65c4d8b59.b9f9bc89d7f3ce4aa1e1af469d10508b.aa1b6a682fcc186e867a9cfd'
// const value =
// '76ebdea75ea529c16f1645d5435413934cbb97378293776cb446331e6d8323865a808b7f0822cd4f36ef6cca57354ea7c955c94d232a55038321184f4294971315fda4b8dc2f33422bbd3a5c978edc0f5bf019f818f0c92f7c570bdc49fe2f71df3856655198b6723fa52697b9225b2e79f04ed80cd35ef61a8e1e06c3751d3f9caa40aa9a41528ce5ab1bfae8ecff0feab784497ccbf2c7dca4461c6b9a74d1ded6233dc38ad75d0fe2f57cfda2bd1d59ed1f8c53de911cf8071dc7cefde7870c303137586bc0c5f85fe93505d9056994a4d8e59df2027b564c303aa723e98ae5934b15039f90d21827ac22b824593f334466622582a2857f04d8bf450310a9924060d113d2ece0259da68376b31a3c2e8a0b79e81f8efe96bf2b7a6b5b1841edca2566a0a89ab80ee6d6ac2ed24e788ad318a1238dfa5b9e965d256935d3aeafe52f986a530b2f9225bbe7a73b43e6515ce789701f501546239460bccc46285f3fcff0c54c2b6df820334a974abea81460fd4ac99a13cb88e296a2275f80f4c783757319417078458977461dd5e101f9f9b7d303578379dc15ee1b549e3fcceffffba895ecc8291d1f82e150ce8e10640fd79f2e0666167ef7d529cd28cb76a72bf2b3e239dbb845bedd39f42c938bbe711542231fb835edb547d919b3c695272f4cd43640eb3bed2b86948278e5aeeb67ef932ef08332730e75752fd5bd247e2dd30470fc493362d2ecac72237dfbf0a1694f8e6b8c18ab24c71eb723a3f610a056a73fea90f4f8660bfc6dec46fa52a55a3d72a5c9b97770bff7d52ea91b97cc643d915341642e201918fcb088986e9134e133519c6575048e3f840622e569fc5fa66e4f0c31681c9de4b1b1f6acb179728bc0a9f9950bf2e0c8d49da835298013977a2f97d7052bcbedbc39b875bbf9a0622ede589810175ab2455ea72a8274659af7066a2c97ba3aacce84531d8685d08b15d3619038997ae459ac916a88bedaf619f574236d1b86b02f29edaf78684c91c6ee45199b861359ee374fecb95be8a85dc012991d22564adeb2ec95f160574858fa010c40ce74077a08f0e681eb4b10a0093e885f115a12469a82b07012ad4e20b836d5711358c47969af89d09c9299edc540321fe897312ca49c9e8f93d50dc68394d0cc1d108f38e3534821b979c405c1f526a3b57651897873aeeb0909b176fb2034f9fdb1d43a8003809af29ff0bdc456a593029c4526b270908ac94b620a7cb8b7da20a6421e0f2671c899c20d78e1e0a1c3ab0b92948c90fca3c36d21a5e53143bc7e061773251364f8ce3b8992b6313c9604dc19c018b9efc6ca8f7c1fe039077fd76775461d1fa3968f51be7e340e3f09b40d6b44c77a7cbf7d71cad2ddea1121b31d27470a38cda8f8d38170a0d8f0b02d56f8e97b2134597d6111ea6670545bd4f7b4e4d6d7cf2fb5b787a68ba8ee84775f06db195f47282d9646dfbdbb57701e2ef5a432b621cb54a70e94de6b7a3f95bfe3c50793e08a27f76f832070fb2d23ed19dcd75f798aeff0c3e76d8215d43e1a8b7e643bffa243691adf2abcd52f465390e92e0344e2f452673f12d50e2ead0496ba76504ae0f78c81f2a2b5a64b39d3fb35f39f178b9a1587d1f07ed28e7696bfc8740f429f6cc35c964ae04ef39c1070eab6415be3aab887ff6192ac4d49ba9b169221b47d73c55f6010dda07a77cbcf54b779d380591786ba70c6d7000af57034539487f801aeb5c097939682e1530bc26c0b030bcab327ddaaaac5c9b969301b6d4ed7f601a46e8431e8cae3800c80c42686fee3b7625742798ced5976d163d2c8aad1bdac36c001e4e246fdb7b4c6850ed99554cfc0c07b7c7acc7659fd042989cc291ec2bd03a59e439860a5c48e430780f9d85c9acb3e85e741e73163f29afb7c43f143e7ada99a4670cbbe2210bfce8d36ee86.a5237f2d798c9f98b26a676794d3c3f6.0f15d0f53657150aeb1462d5'
// const output = decrypt(value, encryptionKey, assocData)
// console.log(output)
// *** TOTP *** //
{
"name": "@1auth/crypto",
"version": "0.0.0-alpha.32",
"version": "0.0.0-alpha.33",
"description": "",
"type": "module",
"engines": {
"node": ">=16"
"node": ">=20"
},

@@ -47,3 +47,3 @@ "engineStrict": true,

"homepage": "https://github.com/willfarrell/1auth",
"gitHead": "3750bef3d7e376c48f7d680e5f2181ee809213b9",
"gitHead": "14b8c5bd83728c460fdcc4c3af5ae5c3c2bb9007",
"dependencies": {

@@ -50,0 +50,0 @@ "@node-rs/argon2": "1.8.3"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc