Skip to content

Commit

Permalink
feat: add Aptos support for Auth
Browse files Browse the repository at this point in the history
  • Loading branch information
ErnoW committed Feb 6, 2023
1 parent bb4e17f commit 055f0f4
Show file tree
Hide file tree
Showing 26 changed files with 983 additions and 94 deletions.
7 changes: 7 additions & 0 deletions .changeset/young-trains-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@moralisweb3/common-aptos-utils': minor
'@moralisweb3/common-auth-utils': minor
'@moralisweb3/auth': minor
---

Add Aptos support for Moralis.Auth
79 changes: 79 additions & 0 deletions packages/auth/integration/mocks/endpoints/requestAptosChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { MockScenarios } from '@moralisweb3/test-utils';
import { createErrorResponse } from '../response/errorResponse';
import { createSimpleAuthResponse } from '../response/simpleAuthResponse';

export const mockRequestAptosChallenge = MockScenarios.create(
{
method: 'post',
name: 'mockRequestAptosChallenge',
url: `/challenge/request/aptos`,
getParams: ({ reqBody }) => {
return {
domain: reqBody?.domain,
chainId: reqBody?.chainId,
address: reqBody?.address,
publicKey: reqBody?.publicKey,
statement: reqBody?.statement,
uri: reqBody?.uri,
expirationTime: reqBody?.expirationTime,
notBefore: reqBody?.notBefore,
resources: reqBody?.resources,
timeout: reqBody?.timeout,
networkType: reqBody?.networkType,
};
},
},
[
{
condition: {
domain: 'defi.finance',
chainId: 'mainnet',
address: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
publicKey: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
statement: 'Please confirm',
uri: 'https://defi.finance/',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
timeout: 15,
},
response: createSimpleAuthResponse(
'Mk5deGOhekYev18pJ',
'defi.finance wants you to sign in with your Aptos account:\n0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d\n\nPlease confirm\n\nURI: https://defi.finance/\nVersion: 1\nChain ID: 1\nNonce: 3c00srSBbEfdOwn4M\nIssued At: 2023-02-06T08:38:56.456Z\nExpiration Time: 2020-01-01T00:00:00.000Z\nNot Before: 2020-01-01T00:00:00.000Z\nResources:\n- https://docs.moralis.io/',
'0x13e04b6cd6f84deef360a444499cbaccae717624f96cfa6dfe7cb250eced74eb',
),
},
{
condition: {
statement: 'INVALID_ADDRESS',
domain: 'defi.finance',
chain: 'mainnet',
address: 'some-address',
uri: 'https://defi.finance/',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
timeout: 15,
networkType: 'aptos',
},
responseStatus: 400,
response: createErrorResponse('INVALID_ADDRESS: some-address'),
},
{
condition: {
statement: 'MULTI_ERROR',
domain: 'defi.finance',
chain: 'mainnet',
address: 'some-address',
uri: 'finance',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
timeout: 15,
networkType: 'aptos',
},
responseStatus: 400,
response: createErrorResponse('MULTI ERROR'),
},
],
);
42 changes: 42 additions & 0 deletions packages/auth/integration/mocks/endpoints/verifyAptosChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MockScenarios } from '@moralisweb3/test-utils';
import { createAptosAuthResponse } from '../response/aptosAuthResponse';
import { createErrorResponse } from '../response/errorResponse';

export const mockVerifyAptosChallenge = MockScenarios.create(
{
method: 'post',
name: 'mockVerifyAptosChallenge',
url: `/challenge/verify/aptos`,
getParams: ({ reqBody }) => {
return {
message: reqBody?.message,
signature: reqBody?.signature,
};
},
},
[
{
condition: {
message: 'VALID_RESPONSE',
signature: '2pH9DqD5rve2qV4yBDshcAjWd2y8TqMx8BPb7f3KoNnuLEhE5JwjruYi4jaFaD4HN6wriLz2Vdr32kRBAJmHcyny',
},
response: createAptosAuthResponse('VALID_RESPONSE'),
},
{
condition: {
message: 'INVALID_SIGNATURE',
signature: 'some-signature',
},
responseStatus: 400,
response: createErrorResponse('INVALID_SIGNATURE: some-signature'),
},
{
condition: {
message: '',
signature: 'some-signature',
},
responseStatus: 400,
response: createErrorResponse('MULTI_ERROR'),
},
],
);
4 changes: 4 additions & 0 deletions packages/auth/integration/mocks/mockServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { mockRequestSolanaChallenge } from './endpoints/requestSolanaChallenge';
import { mockVerifyRemoveBind } from './endpoints/verifyRemoveBind';
import { mockVerifyRequestBind } from './endpoints/verifyRequestBind';
import { mockVerifySolanaChallenge } from './endpoints/verifySolanaChallenge';
import { mockRequestAptosChallenge } from './endpoints/requestAptosChallenge';
import { mockVerifyAptosChallenge } from './endpoints/verifyAptosChallenge';

export const mockServer = MockServer.create({ apiKey: MOCK_API_KEY, apiRoot: AUTH_API_ROOT }, [
mockGetAddresses,
Expand All @@ -20,4 +22,6 @@ export const mockServer = MockServer.create({ apiKey: MOCK_API_KEY, apiRoot: AUT
mockVerifyRemoveBind,
mockVerifyRequestBind,
mockVerifySolanaChallenge,
mockRequestAptosChallenge,
mockVerifyAptosChallenge
]).start();
20 changes: 20 additions & 0 deletions packages/auth/integration/mocks/response/aptosAuthResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const defaultMockAptosAuth = {
id: 'fRyt67D3eRss3RrX',
domain: 'defi.finance',
statement: 'Please confirm',
uri: 'https://defi.finance/',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
version: '1.0',
nonce: '0x1234567890abcdef0123456789abcdef1234567890abcdef',
profileId: '0xbfbcfab169c67072ff418133124480fea02175f1402aaa497daa4fd09026b0e1',
chainId: '1',
address: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
publicKey: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
};

export const createAptosAuthResponse = (tag: string) => ({
...defaultMockAptosAuth,
tag,
});
72 changes: 72 additions & 0 deletions packages/auth/integration/test/requestAptosChallenge.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Auth } from '../../src/Auth';
import { cleanAuth, setupAuth } from '../setup';

describe('requestEvmChallenge', () => {
let AuthApi: Auth;

beforeAll(() => {
AuthApi = setupAuth();
});

afterAll(() => {
cleanAuth();
});

describe('Request Aptos Challenge', () => {
it('should request aptos challenge successfully', async () => {
const result = await AuthApi.requestMessage({
networkType: 'aptos',
domain: 'defi.finance',
statement: 'Please confirm',
uri: 'https://defi.finance/',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
timeout: 15,
chain: 'mainnet',
address: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
publicKey: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
});
expect(result).toBeDefined();
expect(result.result.id).toEqual('Mk5deGOhekYev18pJ');
expect(result.result.message).toEqual("defi.finance wants you to sign in with your Aptos account:\n0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d\n\nPlease confirm\n\nURI: https://defi.finance/\nVersion: 1\nChain ID: 1\nNonce: 3c00srSBbEfdOwn4M\nIssued At: 2023-02-06T08:38:56.456Z\nExpiration Time: 2020-01-01T00:00:00.000Z\nNot Before: 2020-01-01T00:00:00.000Z\nResources:\n- https://docs.moralis.io/");
expect(result.result.profileId).toEqual('0x13e04b6cd6f84deef360a444499cbaccae717624f96cfa6dfe7cb250eced74eb');
});

it('should throw a 400 Error on invalid address', async () => {
expect(
AuthApi.requestMessage({
networkType: 'aptos',
publicKey: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
statement: 'INVALID_ADDRESS',
domain: 'defi.finance',
chain: 'mainnet',
address: 'some-address',
uri: 'https://defi.finance/',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
timeout: 15,
}),
).rejects.toThrowError('C0005] Invalid address provided');
});

it('should throw a 400 Error on multi error', async () => {
expect(
AuthApi.requestMessage({
networkType: 'aptos',
statement: 'MULTI_ERROR',
domain: 'defi.finance',
chain: '1',
address: 'some-address',
publicKey: '0xfb2853744bb8afd58d9386d1856afd8e08de135019961dfa3a10d8c9bf83b99d',
uri: 'finance',
expirationTime: '2020-01-01T00:00:00.000Z',
notBefore: '2020-01-01T00:00:00.000Z',
resources: ['https://docs.moralis.io/'],
timeout: 15,
}),
).rejects.toThrowError('C0005] Invalid address provided');
});
});
});
47 changes: 47 additions & 0 deletions packages/auth/integration/test/verifyAptosChallenge.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Auth } from '../../src/Auth';
import { cleanAuth, setupAuth } from '../setup';

describe('verifyAptosChallenge', () => {
let AuthApi: Auth;

beforeAll(() => {
AuthApi = setupAuth();
});

afterAll(() => {
cleanAuth();
});

describe('Verify Aptos Challenge', () => {
it('should verify aptos challenge successfully', async () => {
const result = await AuthApi.verify({
networkType: 'aptos',
message: 'VALID_RESPONSE',
signature: '2pH9DqD5rve2qV4yBDshcAjWd2y8TqMx8BPb7f3KoNnuLEhE5JwjruYi4jaFaD4HN6wriLz2Vdr32kRBAJmHcyny',
});
expect(result).toBeDefined();
expect(result.result.id).toBeDefined();
expect(result.result.profileId).toBeDefined();
});

it('should throw a 400 Error on invalid signature', async () => {
expect(
AuthApi.verify({
networkType: 'aptos',
message: 'INVALID_SIGNATURE',
signature: 'some-signature',
}),
).rejects.toThrowError('[C0006] Request failed, Bad Request(400): INVALID_SIGNATURE: some-signature');
});

it('should throw a 400 Error on multi error', async () => {
expect(
AuthApi.verify({
networkType: 'aptos',
message: '',
signature: 'some-signature',
}),
).rejects.toThrowError('[C0006] Request failed, Bad Request(400): MULTI_ERROR');
});
});
});
1 change: 1 addition & 0 deletions packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
},
"dependencies": {
"@moralisweb3/api-utils": "^2.12.0",
"@moralisweb3/common-aptos-utils": "workspace:^",
"@moralisweb3/common-auth-utils": "^2.12.0",
"@moralisweb3/common-core": "^2.12.0",
"@moralisweb3/common-evm-utils": "^2.12.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/auth/src/Auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiModule, Core, CoreProvider } from '@moralisweb3/common-core';
import { makeRequestMessage, RequestMessageOptions } from './methods/requestMessage';
import { makeVerify, VerifyEvmOptions, VerifyOptions, VerifySolOptions } from './methods/verify';
import { makeVerify, VerifyEvmOptions, VerifyOptions, VerifySolOptions, VerifyAptosOptions } from './methods/verify';
import {
getAddressesOperation,
GetAddressesRequest,
Expand All @@ -25,6 +25,7 @@ import {
VerifyRequestBindRequest,
VerifyRequestBindResponseAdapter,
verifyRequestBindOperation,
VerifyChallengeAptosResponseAdapter,
} from '@moralisweb3/common-auth-utils';
import { OperationResolver } from '@moralisweb3/api-utils';

Expand Down Expand Up @@ -86,6 +87,7 @@ export class Auth extends ApiModule {
// Function overloading to make typescript happy
public verify(options: VerifyEvmOptions): Promise<VerifyChallengeEvmResponseAdapter>;
public verify(options: VerifySolOptions): Promise<VerifyChallengeSolanaResponseAdapter>;
public verify(options: VerifyAptosOptions): Promise<VerifyChallengeAptosResponseAdapter>;
public verify(options: VerifyOptions) {
return makeVerify(this.core)(options);
}
Expand Down
36 changes: 34 additions & 2 deletions packages/auth/src/methods/requestMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import Core, { AuthErrorCode, MoralisAuthError } from '@moralisweb3/common-core'
import { EvmAddress, EvmAddressish, EvmChain, EvmChainish } from '@moralisweb3/common-evm-utils';
import { BASE_URL } from '../Auth';
import { AuthNetworkType } from '../utils/AuthNetworkType';
import { requestChallengeSolanaOperation, requestChallengeEvmOperation } from '@moralisweb3/common-auth-utils';
import { requestChallengeSolanaOperation, requestChallengeEvmOperation, requestChallengeAptosOperation } from '@moralisweb3/common-auth-utils';
import { AptosAddress, AptosAddressish, AptosNetwork, AptosNetworkish } from '@moralisweb3/common-aptos-utils';

// Imported from Swagger and adjusted for better types for Evm
// TODO: generalize and extend generated types
Expand Down Expand Up @@ -45,7 +46,23 @@ export interface RequestMessageSolOptions {
timeout: number;
}

export type RequestMessageOptions = RequestMessageEvmOptions | RequestMessageSolOptions;
export interface RequestMessageAptosOptions {
networkType: 'aptos';
domain: string;
chain: AptosNetworkish;
address: AptosAddressish;
publicKey: string;
statement?: string;
uri: string;
// TODO: allow Also Date input (and dates-string)
expirationTime?: string;
// TODO: allow Also Date input (and dates-string)
notBefore?: string;
resources?: string[];
timeout: number;
}

export type RequestMessageOptions = RequestMessageEvmOptions | RequestMessageSolOptions | RequestMessageAptosOptions

const makeEvmRequestMessage = (
core: Core,
Expand All @@ -69,6 +86,19 @@ const makeSolRequestMessage = (
});
};


const makeAptosRequestMessage = (
core: Core,
{ address, networkType, chain, ...options }: RequestMessageAptosOptions,
) => {
return new OperationResolver(requestChallengeAptosOperation, BASE_URL, core).fetch({
chainId: AptosNetwork.create(chain).network,
address: AptosAddress.create(address).toString(),
...options,
});
};


export const makeRequestMessage = (core: Core) => async (options: RequestMessageOptions) => {
// Backwards compatibility for the 'network' parameter
if (!options.networkType && options.network) {
Expand All @@ -80,6 +110,8 @@ export const makeRequestMessage = (core: Core) => async (options: RequestMessage
return makeEvmRequestMessage(core, options);
case AuthNetworkType.SOLANA:
return makeSolRequestMessage(core, options);
case AuthNetworkType.APTOS:
return makeAptosRequestMessage(core,options)
default:
if (!options.networkType) {
return makeEvmRequestMessage(core, options);
Expand Down
Loading

0 comments on commit 055f0f4

Please sign in to comment.