Skip to content

Commit

Permalink
TW-1612: Temple Tap AirDrop. + SigAuth middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-tsx committed Jan 5, 2025
1 parent 9863286 commit 7e3af2d
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 35 deletions.
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { redisClient } from './redis';
import { evmRouter } from './routers/evm';
import { adRulesRouter } from './routers/slise-ad-rules';
import { templeWalletAdsRouter } from './routers/temple-wallet-ads';
import { getSigningNonce, tezosSigAuthMiddleware } from './sig-auth';
import { getTkeyStats } from './tkey-stats';
import { getABData } from './utils/ab-test';
import { cancelAliceBobOrder } from './utils/alice-bob/cancel-alice-bob-order';
Expand All @@ -39,7 +40,6 @@ import { coinGeckoTokens } from './utils/gecko-tokens';
import { getExternalApiErrorPayload, isDefined, isNonEmptyString } from './utils/helpers';
import logger from './utils/logger';
import { getSignedMoonPayUrl } from './utils/moonpay/get-signed-moonpay-url';
import { getSigningNonce } from './utils/signing-nonce';
import SingleQueryDataProvider from './utils/SingleQueryDataProvider';
import { getExchangeRates } from './utils/tokens';

Expand Down Expand Up @@ -398,7 +398,7 @@ app.get('/api/signing-nonce', (req, res) => {
}
});

app.post('/api/temple-tap/confirm-airdrop-username', async (req, res) => {
app.post('/api/temple-tap/confirm-airdrop-username', tezosSigAuthMiddleware, async (req, res) => {
try {
const response = await fetch(new URL('v1/confirm-airdrop-address', EnvVars.TEMPLE_TAP_API_URL + '/'), {
method: 'POST',
Expand Down
2 changes: 1 addition & 1 deletion src/magic-square.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { verifySignature, getPkhfromPk } from '@taquito/utils';
import { StatusCodes } from 'http-status-codes';

import { objectStorageMethodsFactory } from './redis';
import { getSigningNonce, removeSigningNonce } from './sig-auth';
import { CodedError } from './utils/errors';
import { safeCheck } from './utils/helpers';
import { getSigningNonce, removeSigningNonce } from './utils/signing-nonce';

interface Participant {
pkh: string;
Expand Down
82 changes: 82 additions & 0 deletions src/sig-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { randomStringForEntropy } from '@stablelib/random';
import { getPkhfromPk, validateAddress, ValidationResult, verifySignature } from '@taquito/utils';
import { NextFunction, Request, Response } from 'express';
import { StatusCodes } from 'http-status-codes';
import memoizee from 'memoizee';
import * as yup from 'yup';

import { CodedError } from './utils/errors';

const SIGNING_NONCE_TTL = 5 * 60_000;

export const getSigningNonce = memoizee(
(pkh: string) => {
if (validateAddress(pkh) !== ValidationResult.VALID) throw new CodedError(400, 'Invalid address');

return buildNonce();
},
{
max: 1_000_000,
maxAge: SIGNING_NONCE_TTL
}
);

export function removeSigningNonce(pkh: string) {
getSigningNonce.delete(pkh);
}

export async function tezosSigAuthMiddleware(req: Request, res: Response, next: NextFunction) {
const sigHeaders = await sigAuthHeadersSchema.validate(req.headers).catch(() => null);

if (!sigHeaders) return void res.status(StatusCodes.UNAUTHORIZED).send();

const { 'tw-tez-sig-pk': publicKey, 'tw-tez-sig-msg': messageBytes, 'tw-tez-sig-sig': signature } = sigHeaders;

let pkh: string;
try {
pkh = getPkhfromPk(publicKey);
} catch (err) {
console.error(err);

return void res.status(StatusCodes.BAD_REQUEST).send({ message: 'Invalid public key' });
}

// Nonce
const { value: nonce } = getSigningNonce(pkh);
const nonceBytes = Buffer.from(nonce, 'utf-8').toString('hex');

console.log('messageBytes=', messageBytes, '|', nonce);

if (!messageBytes.includes(nonceBytes))
return void res.status(StatusCodes.UNAUTHORIZED).send({ code: 'INVALID_NONCE', message: 'Invalid message nonce' });

// Signature
try {
verifySignature(messageBytes, publicKey, signature);
} catch (error) {
console.error(error);

return void res
.status(StatusCodes.UNAUTHORIZED)
.send({ code: 'INVALID_SIG', message: 'Invalid signature or message' });
}

removeSigningNonce(pkh);

next();
}

const sigAuthHeadersSchema = yup.object({
'tw-tez-sig-pk': yup.string().required(),
'tw-tez-sig-msg': yup.string().required(),
'tw-tez-sig-sig': yup.string().required()
});

function buildNonce() {
// Same as in in SIWE.generateNonce()
const value = randomStringForEntropy(96);

const expiresAt = new Date(Date.now() + SIGNING_NONCE_TTL).toISOString();

return { value, expiresAt };
}
32 changes: 0 additions & 32 deletions src/utils/signing-nonce.ts

This file was deleted.

0 comments on commit 7e3af2d

Please sign in to comment.