Merge branch 'feature/auth'
This commit is contained in:
@@ -94,6 +94,97 @@ export const apiKeyAuthMacro = new Elysia().macro({
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
const apiKey = headers['x-api-key']
|
||||
const nonce = headers['x-nonce']
|
||||
const signature = headers['x-signature']
|
||||
const rawTimestamp = headers['x-timestamp']
|
||||
|
||||
const missingHeaders = []
|
||||
if (!apiKey) {
|
||||
missingHeaders.push('X-API-Key')
|
||||
}
|
||||
if (!nonce) {
|
||||
missingHeaders.push('X-Nonce')
|
||||
}
|
||||
if (!rawTimestamp) {
|
||||
missingHeaders.push('X-Timestamp')
|
||||
}
|
||||
if (!signature) {
|
||||
missingHeaders.push('X-Signature')
|
||||
}
|
||||
if (!apiKey || !nonce || !signature || !rawTimestamp) {
|
||||
const missing = missingHeaders.join(', ')
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to missing auth headers: ' + missing)
|
||||
throw new UnauthenticatedError('Missing auth headers: ' + missing)
|
||||
}
|
||||
|
||||
const timestamp = Number(rawTimestamp)
|
||||
if (!Number.isFinite(timestamp)) {
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to invalid timestamp')
|
||||
throw new UnauthenticatedError('Invalid timestamp')
|
||||
}
|
||||
const nowSec = Math.floor(Date.now() / 1000)
|
||||
if (Math.abs(nowSec - timestamp) > apiAuth.skewSeconds) {
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to timestamp out of range')
|
||||
throw new UnauthenticatedError('Timestamp out of range')
|
||||
}
|
||||
|
||||
const merchant = await db.query.merchants.findFirst({
|
||||
where: (t, { and, eq }) => and(
|
||||
eq(t.apiKey, apiKey),
|
||||
eq(t.isActive, true),
|
||||
),
|
||||
})
|
||||
if (!merchant) {
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to invalid API key')
|
||||
throw new UnauthenticatedError('Invalid API key')
|
||||
}
|
||||
|
||||
const existingNonce = await db.$count($t.merchantNonces, and(
|
||||
eq($t.merchantNonces.merchantId, merchant.merchantId),
|
||||
eq($t.merchantNonces.nonce, nonce),
|
||||
))
|
||||
if (existingNonce > 0) {
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to replay attack detected')
|
||||
throw new UnauthenticatedError('Duplicate request detected, ensure your nonce is unique')
|
||||
}
|
||||
|
||||
db.insert(merchantNonces).values({
|
||||
merchantId: merchant.merchantId,
|
||||
nonce,
|
||||
}).catch((error) => {
|
||||
logger.error({
|
||||
errorMessage: error.message,
|
||||
}, 'Failed to save merchant nonce into database')
|
||||
})
|
||||
|
||||
if (!verify(body, timestamp, nonce, signature, merchant.secretKey)) {
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to invalid signature')
|
||||
throw new UnauthenticatedError('Invalid signature')
|
||||
}
|
||||
},
|
||||
}),
|
||||
verifyKeyQuery: (enabled: boolean) => ({
|
||||
async beforeHandle({ headers, query }) {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
>>>>>>> feature/auth
|
||||
const apiKey = headers['x-api-key']
|
||||
const nonce = headers['x-nonce']
|
||||
const signature = headers['x-signature']
|
||||
@@ -153,7 +244,7 @@ export const apiKeyAuthMacro = new Elysia().macro({
|
||||
}, 'Failed to save merchant nonce into database')
|
||||
})
|
||||
|
||||
if (!verify(body, timestamp, nonce, signature, merchant.secretKey)) {
|
||||
if (!verify(query, timestamp, nonce, signature, merchant.secretKey)) {
|
||||
logger.debug({
|
||||
'headers.authorization': headers.authorization,
|
||||
}, 'Failed API key authentication attempt due to invalid signature')
|
||||
|
||||
Reference in New Issue
Block a user