Skip to content

Commit

Permalink
Merge pull request #2 from algorandfoundation/feat/rekeyed-account-su…
Browse files Browse the repository at this point in the history
…pport

Feature: Rekeyed Account Support
  • Loading branch information
PhearZero authored Apr 15, 2024
2 parents 38f114e + 7dfbff9 commit a94ddc8
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 46 deletions.
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion services/liquid-auth-api-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"@types/ua-parser-js": "^0.7.37",
"@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.3",
"algosdk": "^2.6.0",
"algosdk": "^2.7.0",
"cross-env": "^7.0.3",
"eslint": "^8.50.0",
"eslint-config-prettier": "^9.0.0",
Expand Down
14 changes: 14 additions & 0 deletions services/liquid-auth-api-js/src/algod/algod.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import algosdk from 'algosdk';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AlgodService extends algosdk.Algodv2 {
constructor(private configService: ConfigService) {
const token = configService.get('algod.token') || '';
const server = configService.get('algod.server');
const port = configService.get('algod.port');
super(token, server, port);
}
}

7 changes: 6 additions & 1 deletion services/liquid-auth-api-js/src/config/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export default () => ({
username: process.env.DB_USERNAME || 'algorand',
password: process.env.DB_PASSWORD || 'algorand',
name: process.env.DB_NAME || 'fido',
atlas: process.env.DB_ATLAS === 'true'
atlas: process.env.DB_ATLAS === 'true',
},
algod: {
token: process.env.ALGOD_TOKEN || '',
server: process.env.ALGOD_SERVER || 'https://testnet-api.algonode.cloud',
port: process.env.ALGOD_PORT || '443',
},
});
114 changes: 73 additions & 41 deletions services/liquid-auth-api-js/src/connect/connect.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import {
Session,
Inject,
Logger,
Res,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { Response } from 'express';
import { AuthService } from '../auth/auth.service.js';
import { AlgodService } from '../algod/algod.service.js';
import { AlgorandEncoder } from './AlgoEncoder.js';
import * as nacl from 'tweetnacl';

const algoEncoder = new AlgorandEncoder();

Expand All @@ -21,7 +23,6 @@ const base64ToUint8Array = (encoded) => {
.map((c) => c.charCodeAt(0)),
);
};
import nacl from 'tweetnacl';

type LinkResponseDTO = {
credId?: string;
Expand All @@ -37,6 +38,7 @@ export class ConnectController {

constructor(
private authService: AuthService,
private algodService: AlgodService,
@Inject('ACCOUNT_LINK_SERVICE') private client: ClientProxy,
) {}

Expand All @@ -51,57 +53,87 @@ export class ConnectController {
*/
@Post('response')
async linkWalletResponse(
@Res() res: Response,
@Session() session: Record<string, any>,
@Body()
{ requestId, wallet, challenge, signature, credId }: LinkResponseDTO,
) {
try {
this.logger.log(
`POST /connect/response for RequestId: ${requestId} Session: ${session.id} with Wallet: ${wallet}`,
);
// Decode Address
const publicKey = algoEncoder.decodeAddress(wallet);
this.logger.log(
`POST /connect/response for RequestId: ${requestId} Session: ${session.id} with Wallet: ${wallet}`,
);

// Decode signature
const uint8Signature = base64ToUint8Array(
signature.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''),
);
// Decode Address
const publicKey = algoEncoder.decodeAddress(wallet);

// Decode signature
const uint8Signature = base64ToUint8Array(
signature.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''),
);

// Validate Signature
const encoder = new TextEncoder();
const encodedChallenge = encoder.encode(challenge);

if (
!nacl.sign.detached.verify(encodedChallenge, uint8Signature, publicKey)
) {
// signature check failed, check if its rekeyed
// if it is, verify against that public key instead
let accountInfo;
try {
accountInfo = await this.algodService
.accountInformation(wallet)
.exclude('all')
.do();
} catch (e) {
throw new HttpException(
'Failed to fetch Account Info',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}

if (!accountInfo['auth-addr']) {
throw new HttpException('Invalid signature', HttpStatus.FORBIDDEN);
}

const authPublicKey = algoEncoder.decodeAddress(accountInfo['auth-addr']);

// Validate Signature
const encoder = new TextEncoder();
// Validate Auth Address Signature
if (
!nacl.sign.detached.verify(
encoder.encode(challenge),
encodedChallenge,
uint8Signature,
publicKey,
authPublicKey,
)
) {
return res
.status(401)
.json({
error: 'Invalid signature',
})
.end();
} else {
this.logger.log('AUTH Wallet is attested');
// Authenticated user
await this.authService.init(wallet);

const parsedRequest =
typeof requestId === 'string' ? parseFloat(requestId) : requestId;
console.log('Request Forwarding', parsedRequest);
session.wallet = wallet;
session.active = true;
this.client.emit<string>('auth', {
requestId,
wallet,
credId,
});
return res.status(200).end();
throw new HttpException('Invalid signature', HttpStatus.FORBIDDEN);
}
}

this.logger.log('AUTH Wallet is attested');
// Authenticated user
try {
await this.authService.init(wallet);
} catch (e) {
res.status(500).json({ error: e.message });
throw new HttpException(
'Failed to initialize wallet',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}

const parsedRequest =
typeof requestId === 'string' ? parseFloat(requestId) : requestId;

console.log('Request Forwarding', parsedRequest);

session.wallet = wallet;
session.active = true;

this.client.emit<string>('auth', {
requestId,
wallet,
credId,
});

return;
}
}
3 changes: 2 additions & 1 deletion services/liquid-auth-api-js/src/connect/connect.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Session, SessionSchema } from './session.schema.js';
import { SessionService } from './session.service.js';
import { AuthService } from '../auth/auth.service.js';
import { User, UserSchema } from '../auth/auth.schema.js';
import { AlgodService } from '../algod/algod.service.js';

@Module({
imports: [
Expand All @@ -29,6 +30,6 @@ import { User, UserSchema } from '../auth/auth.schema.js';
]),
],
controllers: [ConnectController],
providers: [AuthService, SessionService, ConnectGateway],
providers: [AuthService, SessionService, ConnectGateway, AlgodService],
})
export class ConnectModule {}
6 changes: 6 additions & 0 deletions sites/dapp-ui/src/pages/home/ConnectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {useSocket} from '../../hooks/useSocket';
import nacl from 'tweetnacl';
import { StateContext } from '../../Contexts';
import { useCredentialStore, Credential } from '../../store';

const style = {
position: 'absolute' as const,
top: '50%',
Expand All @@ -21,6 +22,7 @@ const style = {
border: '2px solid #000',
boxShadow: 24,
};

export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'}) {
const {socket} = useSocket();
const credentials = useCredentialStore((state)=> state.addresses);
Expand Down Expand Up @@ -119,6 +121,7 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box>
<Fade in={open}>
<Box sx={style}>
<Box sx={{
Expand All @@ -145,9 +148,12 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar
transform: 'translate(-50%, -50%)',
top: "50%",
}}/>

</Box>

</Box>
</Fade>
</Box>
</Modal>
</div>
);
Expand Down

0 comments on commit a94ddc8

Please sign in to comment.