From dd3602a29c5d5fd5d30ebd5abcb748a7042e11db Mon Sep 17 00:00:00 2001 From: Shalom Ogunshola Date: Tue, 6 Sep 2022 15:33:55 +0100 Subject: [PATCH 1/6] feat: SolAuth Implement solana auth in auth package --- .changeset/poor-fans-talk.md | 5 + .../src/auth/MoralisAuthAdapter.ts | 4 +- packages/auth/src/generated/types.ts | 258 +++++++++++++++++- packages/auth/src/methods/requestMessage.ts | 46 +++- packages/auth/src/methods/verify.ts | 50 +++- .../auth/src/resolvers/evmRequestChallenge.ts | 2 +- .../auth/src/resolvers/evmVerifyChallenge.ts | 3 +- packages/auth/src/resolvers/index.ts | 4 + .../auth/src/resolvers/solRequestChallenge.ts | 46 ++++ .../auth/src/resolvers/solVerifyChallenge.ts | 37 +++ 10 files changed, 424 insertions(+), 31 deletions(-) create mode 100644 .changeset/poor-fans-talk.md create mode 100644 packages/auth/src/resolvers/index.ts create mode 100644 packages/auth/src/resolvers/solRequestChallenge.ts create mode 100644 packages/auth/src/resolvers/solVerifyChallenge.ts diff --git a/.changeset/poor-fans-talk.md b/.changeset/poor-fans-talk.md new file mode 100644 index 0000000000..6f185b1c0a --- /dev/null +++ b/.changeset/poor-fans-talk.md @@ -0,0 +1,5 @@ +--- +'@moralisweb3/auth': major +--- + +Added solana authentication to auth package diff --git a/demos/parse-server/src/auth/MoralisAuthAdapter.ts b/demos/parse-server/src/auth/MoralisAuthAdapter.ts index 75c1a33e68..f95c5211c5 100644 --- a/demos/parse-server/src/auth/MoralisAuthAdapter.ts +++ b/demos/parse-server/src/auth/MoralisAuthAdapter.ts @@ -15,9 +15,9 @@ function validateAuthData(authData: any) { const data = result.toJSON(); if (id === data.profileId && authId === data.id) { - authData.chainId = result.result.chain.decimal; + authData.chainId = result.result.chain?.decimal; authData.nonce = data.nonce; - authData.address = result.result.address.checksum; + authData.address = result.result.address.format(); authData.version = data.version; authData.domain = data.domain; authData.expirationTime = data.expirationTime; diff --git a/packages/auth/src/generated/types.ts b/packages/auth/src/generated/types.ts index c1afc3dd94..0a65906e63 100644 --- a/packages/auth/src/generated/types.ts +++ b/packages/auth/src/generated/types.ts @@ -13,6 +13,12 @@ export interface paths { "/challenge/verify/evm": { post: operations["verifyChallengeEvm"]; }; + "/challenge/request/solana": { + post: operations["requestChallengeSolana"]; + }; + "/challenge/verify/solana": { + post: operations["verifyChallengeSolana"]; + }; } export interface components { @@ -27,8 +33,24 @@ export interface components { /** * @description EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved. * @example 1 + * @enum {string} */ - chainId: number; + chainId: + | "ETH" + | "ROPSTEN" + | "RINKEBY" + | "GOERLI" + | "CRONOS" + | "KOVAN" + | "BSC" + | "BSC_TESTNET" + | "POLYGON" + | "FANTOM" + | "CRONOS_TESTNET" + | "LOCALDEVCHAIN" + | "AVALANCHE_TESTNET" + | "AVALANCHE" + | "MUMBAI"; /** * @description Ethereum address performing the signing conformant to capitalization encoded checksum specified in EIP-55 where applicable. * @example 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B @@ -75,8 +97,8 @@ export interface components { }; EvmChallengeResponseDto: { /** - * @description Secret Challenge ID used to identify this particular request. Is should be used at the backend of the calling service to identify the completed request. - * @example fRyt67D3eRss3RrX + * @description 17-characters Alphanumeric string Secret Challenge ID used to identify this particular request. Is should be used at the backend of the calling service to identify the completed request. + * @example fRyt67D3eRss3RrXa */ id: string; /** @@ -110,7 +132,7 @@ export interface components { }; EvmCompleteChallengeResponseDto: { /** - * @description Secret Challenge ID used to identify this particular request. Is should be used at the backend of the calling service to identify the completed request. + * @description 17-characters Alphanumeric string Secret Challenge ID used to identify this particular request. Is should be used at the backend of the calling service to identify the completed request. * @example fRyt67D3eRss3RrX */ id: string; @@ -123,8 +145,24 @@ export interface components { /** * @description EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved. * @example 1 + * @enum {string} */ - chainId: number; + chainId: + | "ETH" + | "ROPSTEN" + | "RINKEBY" + | "GOERLI" + | "CRONOS" + | "KOVAN" + | "BSC" + | "BSC_TESTNET" + | "POLYGON" + | "FANTOM" + | "CRONOS_TESTNET" + | "LOCALDEVCHAIN" + | "AVALANCHE_TESTNET" + | "AVALANCHE" + | "MUMBAI"; /** * @description Ethereum address performing the signing conformant to capitalization encoded checksum specified in EIP-55 where applicable. * @example 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B @@ -175,6 +213,184 @@ export interface components { */ profileId: string; }; + SolanaChallengeRequestDto: { + /** + * Format: hostname + * @description RFC 4501 dns authority that is requesting the signing. + * @example defi.finance + */ + domain: string; + /** + * @description The network where Contract Accounts must be resolved. + * @example mainnet + * @enum {string} + */ + network: "mainnet" | "testnet" | "devnet"; + /** + * @description Solana public key with a length of 44 characters that is used to perform the signing + * @example 26qv4GCcx98RihuK3c4T6ozB3J7L6VwCuFVc7Ta2A3Uo + */ + address: string; + /** + * @description Human-readable ASCII assertion that the user will sign, and it must not contain ` + * `. + * @example Please confirm + */ + statement?: string; + /** + * Format: uri + * @description RFC 3986 URI referring to the resource that is the subject of the signing (as in the __subject__ of a claim). + * @example https://defi.finance/ + */ + uri: string; + /** + * Format: date-time + * @description ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid. + * @example 2020-01-01T00:00:00.000Z + */ + expirationTime?: string; + /** + * Format: date-time + * @description ISO 8601 datetime string that, if present, indicates when the signed authentication message will become valid. + * @example 2020-01-01T00:00:00.000Z + */ + notBefore?: string; + /** + * @description List of information or references to information the user wishes to have resolved as part of authentication by the relying party. They are expressed as RFC 3986 URIs separated by ` + * - `. + * @example [ + * "https://docs.moralis.io/" + * ] + */ + resources?: string[]; + /** + * @description Time in seconds before the challenge is expired + * @default 15 + * @example 15 + */ + timeout: number; + }; + SolanaChallengeResponseDto: { + /** + * @description 17-characters Alphanumeric string Secret Challenge ID used to identify this particular request. Is should be used at the backend of the calling service to identify the completed request. + * @example fRyt67D3eRss3RrX + */ + id: string; + /** + * @description Message that needs to be signed by the end user + * @example defi.finance wants you to sign in with your Solana account: + * 26qv4GCcx98RihuK3c4T6ozB3J7L6VwCuFVc7Ta2A3Uo + * + * I am a third party API + * + * URI: http://defi.finance + * Version: 1 + * Network: mainnet + * Nonce: PYxxb9msdjVXsMQ9x + * Issued At: 2022-08-25T11:02:34.097Z + * Expiration Time: 2022-08-25T11:12:38.243Z + * Resources: + * - https://docs.moralis.io/ + */ + message: string; + /** + * @description Unique identifier with a length of 66 characters + * @example 0xbfbcfab169c67072ff418133124480fea02175f1402aaa497daa4fd09026b0e1 + */ + profileId: string; + }; + SolanaCompleteChallengeRequestDto: { + /** + * @description Message that needs to be signed by the end user + * @example defi.finance wants you to sign in with your Solana account: + * 26qv4GCcx98RihuK3c4T6ozB3J7L6VwCuFVc7Ta2A3Uo + * + * I am a third party API + * + * URI: http://defi.finance + * Version: 1 + * Network: mainnet + * Nonce: PYxxb9msdjVXsMQ9x + * Issued At: 2022-08-25T11:02:34.097Z + * Expiration Time: 2022-08-25T11:12:38.243Z + * Resources: + * - https://docs.moralis.io/ + */ + message: string; + /** + * @description Base58 signature that needs to be used to verify end user + * @example 2pH9DqD5rve2qV4yBDshcAjWd2y8TqMx8BPb7f3KoNnuLEhE5JwjruYi4jaFaD4HN6wriLz2Vdr32kRBAJmHcyny + */ + signature: string; + }; + SolanaCompleteChallengeResponseDto: { + /** + * @description 17-characters Alphanumeric string Secret Challenge ID used to identify this particular request. Is should be used at the backend of the calling service to identify the completed request. + * @example fRyt67D3eRss3RrX + */ + id: string; + /** + * Format: hostname + * @description RFC 4501 dns authority that is requesting the signing. + * @example defi.finance + */ + domain: string; + /** + * @description The network where Contract Accounts must be resolved. + * @example mainnet + * @enum {string} + */ + network: "mainnet" | "testnet" | "devnet"; + /** + * @description Solana public key with a length of 44 characters that is used to perform the signing + * @example 26qv4GCcx98RihuK3c4T6ozB3J7L6VwCuFVc7Ta2A3Uo + */ + address: string; + /** + * @description Human-readable ASCII assertion that the user will sign, and it must not contain ` + * `. + * @example Please confirm + */ + statement?: string; + /** + * Format: uri + * @description RFC 3986 URI referring to the resource that is the subject of the signing (as in the __subject__ of a claim). + * @example https://defi.finance/ + */ + uri: string; + /** + * Format: date-time + * @description ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid. + * @example 2020-01-01T00:00:00.000Z + */ + expirationTime?: string; + /** + * Format: date-time + * @description ISO 8601 datetime string that, if present, indicates when the signed authentication message will become valid. + * @example 2020-01-01T00:00:00.000Z + */ + notBefore?: string; + /** + * @description List of information or references to information the user wishes to have resolved as part of authentication by the relying party. They are expressed as RFC 3986 URIs separated by ` + * - `. + * @example [ + * "https://docs.moralis.io/" + * ] + */ + resources?: string[]; + /** + * @description EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved. + * @example 1.0 + */ + version: string; + /** @example 0x1234567890abcdef0123456789abcdef1234567890abcdef */ + nonce: string; + /** + * @description Unique identifier with a length of 66 characters + * @example 0xbfbcfab169c67072ff418133124480fea02175f1402aaa497daa4fd09026b0e1 + */ + profileId: string; + }; }; } @@ -305,6 +521,38 @@ export interface operations { }; }; }; + requestChallengeSolana: { + parameters: {}; + responses: { + /** The back channel challenge containing the id to store on the api and the message to be signed by the user */ + 201: { + content: { + "application/json": components["schemas"]["SolanaChallengeResponseDto"]; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SolanaChallengeRequestDto"]; + }; + }; + }; + verifyChallengeSolana: { + parameters: {}; + responses: { + /** The token to be used to call the third party API from the client */ + 201: { + content: { + "application/json": components["schemas"]["SolanaCompleteChallengeResponseDto"]; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SolanaCompleteChallengeRequestDto"]; + }; + }; + }; } export interface external {} diff --git a/packages/auth/src/methods/requestMessage.ts b/packages/auth/src/methods/requestMessage.ts index 64098bf8d6..1268ec6128 100644 --- a/packages/auth/src/methods/requestMessage.ts +++ b/packages/auth/src/methods/requestMessage.ts @@ -1,11 +1,13 @@ +import { SolAddressish, SolNetworkish, SolAddress, SolNetwork } from '@moralisweb3/sol-utils'; import { EndpointResolver } from '@moralisweb3/api-utils'; import MoralisCore, { AuthErrorCode, MoralisAuthError } from '@moralisweb3/core'; import { EvmAddress, EvmAddressish, EvmChain, EvmChainish } from '@moralisweb3/evm-utils'; import { BASE_URL } from '../MoralisAuth'; -import { initializeChallenge } from '../resolvers/evmRequestChallenge'; +import { initializeChallengeEvm, initializeChallengeSol } from '../resolvers'; export enum AuthNetwork { EVM = 'evm', + SOLANA = 'solana', } // Imported from Swagger and adjusted for better types for Evm @@ -15,34 +17,62 @@ export interface RequestMessageEvmOptions { domain: string; chain: EvmChainish; address: EvmAddressish; - statement?: string | undefined; + 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[] | undefined; + resources?: string[]; timeout: number; } -export type RequestMessageOptions = RequestMessageEvmOptions; +export interface RequestMessageSolOptions { + network: 'solana'; + domain: string; + solNetwork: SolNetworkish; + address: SolAddressish; + 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; -// eslint-disable-next-line @typescript-eslint/no-unused-vars const makeEvmRequestMessage = ( core: MoralisCore, { chain, address, network, ...options }: RequestMessageEvmOptions, ) => { - return EndpointResolver.create(core, BASE_URL, initializeChallenge).fetch({ - chainId: EvmChain.create(chain).decimal, + return EndpointResolver.create(core, BASE_URL, initializeChallengeEvm).fetch({ + // TODO: remove this when the API is fixed + chainId: EvmChain.create(chain).decimal as any, address: EvmAddress.create(address).checksum, ...options, }); }; +const makeSolRequestMessage = ( + core: MoralisCore, + { address, solNetwork, network, ...options }: RequestMessageSolOptions, +) => { + return EndpointResolver.create(core, BASE_URL, initializeChallengeSol).fetch({ + network: SolNetwork.create(solNetwork).network, + address: SolAddress.create(address).toString(), + ...options, + }); +}; + export const makeRequestMessage = (core: MoralisCore) => (options: RequestMessageOptions) => { switch (options.network) { - case 'evm': + case AuthNetwork.EVM: return makeEvmRequestMessage(core, options); + case AuthNetwork.SOLANA: + return makeSolRequestMessage(core, options); default: throw new MoralisAuthError({ code: AuthErrorCode.INCORRECT_NETWORK, diff --git a/packages/auth/src/methods/verify.ts b/packages/auth/src/methods/verify.ts index 4f39a0acaf..c403dee509 100644 --- a/packages/auth/src/methods/verify.ts +++ b/packages/auth/src/methods/verify.ts @@ -1,29 +1,51 @@ import { EndpointResolver } from '@moralisweb3/api-utils'; -import MoralisCore, { assertUnreachable } from '@moralisweb3/core'; +import MoralisCore, { AuthErrorCode, MoralisAuthError } from '@moralisweb3/core'; import { BASE_URL } from '../MoralisAuth'; -import { completeChallenge } from '../resolvers/evmVerifyChallenge'; +import { completeChallengeEvm, completeChallengeSol } from '../resolvers'; +import { AuthNetwork } from './requestMessage'; -export interface VerifyEvmOptions { +export interface VerifEvmOptions { message: string; signature: string; network: 'evm'; } -export type VerifyOptions = VerifyEvmOptions; +export interface VerifySolOptions { + message: string; + signature: string; + network: 'solana'; +} + +export type VerifyOptions = VerifEvmOptions | VerifySolOptions; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const makeEvmVerify = (core: MoralisCore, { network, ...options }: VerifyEvmOptions) => { - return EndpointResolver.create(core, BASE_URL, completeChallenge).fetch({ +const makeEvmVerify = (core: MoralisCore, { network, ...options }: VerifEvmOptions) => { + return EndpointResolver.create(core, BASE_URL, completeChallengeEvm).fetch({ message: options.message, signature: options.signature, }); }; -export const makeVerify = (core: MoralisCore) => (options: VerifyOptions) => { - switch (options.network) { - case 'evm': - return makeEvmVerify(core, options); - default: - return assertUnreachable(options.network); - } +const makeSolVerify = (core: MoralisCore, { network, ...options }: VerifySolOptions) => { + return EndpointResolver.create(core, BASE_URL, completeChallengeSol).fetch({ + message: options.message, + signature: options.signature, + }); }; + +export const makeVerify = + (core: MoralisCore) => + (options: VerifyOptions) => { + switch (options.network) { + case AuthNetwork.EVM: + return makeEvmVerify(core, options); + case AuthNetwork.SOLANA: + return makeSolVerify(core, options); + default: + throw new MoralisAuthError({ + code: AuthErrorCode.INCORRECT_NETWORK, + message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) + .map((value) => `"${value}"`) + .join(', ')}`, + }); + } + }; diff --git a/packages/auth/src/resolvers/evmRequestChallenge.ts b/packages/auth/src/resolvers/evmRequestChallenge.ts index 60785c67cd..f11f456534 100644 --- a/packages/auth/src/resolvers/evmRequestChallenge.ts +++ b/packages/auth/src/resolvers/evmRequestChallenge.ts @@ -31,7 +31,7 @@ const apiToResult = (apiData: ApiResult) => { }; }; -export const initializeChallenge = createEndpointFactory(() => +export const initializeChallengeEvm = createEndpointFactory(() => createEndpoint({ name, getUrl: () => `/challenge/request/evm`, diff --git a/packages/auth/src/resolvers/evmVerifyChallenge.ts b/packages/auth/src/resolvers/evmVerifyChallenge.ts index 158408979f..2e84e3d483 100644 --- a/packages/auth/src/resolvers/evmVerifyChallenge.ts +++ b/packages/auth/src/resolvers/evmVerifyChallenge.ts @@ -14,7 +14,7 @@ const bodyParams = ['message', 'signature'] as const; type ApiResult = operations[Name]['responses']['201']['content']['application/json']; -export const completeChallenge = createEndpointFactory(() => +export const completeChallengeEvm = createEndpointFactory(() => createEndpoint({ name: 'Verify Challenge (EVM)', getUrl: () => `/challenge/verify/evm`, @@ -22,6 +22,7 @@ export const completeChallenge = createEndpointFactory(() => ...data, // TODO: revisit EVM logic once we know how authentication in other networks work chain: EvmChain.create(chainId), + solNetwork: undefined, address: EvmAddress.create(data.address), expirationTime: maybe(data.expirationTime, (value) => new Date(value)), }), diff --git a/packages/auth/src/resolvers/index.ts b/packages/auth/src/resolvers/index.ts new file mode 100644 index 0000000000..970478d373 --- /dev/null +++ b/packages/auth/src/resolvers/index.ts @@ -0,0 +1,4 @@ +export * from './evmRequestChallenge'; +export * from './evmVerifyChallenge'; +export * from './solRequestChallenge'; +export * from './solVerifyChallenge'; diff --git a/packages/auth/src/resolvers/solRequestChallenge.ts b/packages/auth/src/resolvers/solRequestChallenge.ts new file mode 100644 index 0000000000..04ebb764a1 --- /dev/null +++ b/packages/auth/src/resolvers/solRequestChallenge.ts @@ -0,0 +1,46 @@ +import { createEndpoint, createEndpointFactory } from '@moralisweb3/api-utils'; +import { toCamelCase } from '@moralisweb3/core'; +import { operations } from '../generated/types'; + +const name = 'requestChallengeSolana'; + +type Name = typeof name; +type BodyParams = operations[Name]['requestBody']['content']['application/json']; +type ApiParams = BodyParams; +const method = 'post'; +const bodyParams = [ + 'domain', + 'network', + 'address', + 'statement', + 'uri', + 'expirationTime', + 'notBefore', + 'resources', + 'timeout', +] as const; +type Params = ApiParams; + +type ApiResult = operations[Name]['responses']['201']['content']['application/json']; + +const apiToResult = (apiData: ApiResult) => { + const data = toCamelCase(apiData); + + return { + ...data, + }; +}; + +export const initializeChallengeSol = createEndpointFactory(() => + createEndpoint({ + name, + getUrl: () => `/challenge/request/solana`, + apiToResult, + resultToJson: (data) => ({ + ...data, + }), + parseParams: (params: Params): ApiParams => params, + method, + bodyParams, + }), +); diff --git a/packages/auth/src/resolvers/solVerifyChallenge.ts b/packages/auth/src/resolvers/solVerifyChallenge.ts new file mode 100644 index 0000000000..eadf1f4fad --- /dev/null +++ b/packages/auth/src/resolvers/solVerifyChallenge.ts @@ -0,0 +1,37 @@ +import { SolNetwork, SolAddress } from '@moralisweb3/sol-utils'; +import { maybe, toCamelCase } from '@moralisweb3/core'; +import { createEndpoint, createEndpointFactory } from '@moralisweb3/api-utils'; +import { operations } from '../generated/types'; + +const name = 'verifyChallengeSolana'; + +type Name = typeof name; +type BodyParams = operations[Name]['requestBody']['content']['application/json']; +type ApiParams = BodyParams; +type Params = ApiParams; +const method = 'post'; +const bodyParams = ['message', 'signature'] as const; + +type ApiResult = operations[Name]['responses']['201']['content']['application/json']; + +export const completeChallengeSol = createEndpointFactory(() => + createEndpoint({ + name: 'Verify Challenge (Solana)', + getUrl: () => `/challenge/verify/solana`, + apiToResult: ({ network, ...data }: ApiResult) => ({ + ...data, + solNetwork: SolNetwork.create(network), + chain: undefined, + address: SolAddress.create(data.address), + expirationTime: maybe(data.expirationTime, (value) => new Date(value)), + }), + resultToJson: (result) => ({ + ...toCamelCase(result), + solNetwork: result.solNetwork.format(), + address: result.address.format(), + }), + parseParams: (params: Params): ApiParams => params, + method, + bodyParams, + }), +); From 2859e754f5caba00fd80b5751b500cb9e706decd Mon Sep 17 00:00:00 2001 From: Shalom Ogunshola Date: Tue, 6 Sep 2022 15:46:55 +0100 Subject: [PATCH 2/6] lint fix --- packages/auth/src/generated/types.ts | 60 ++++++++++----------- packages/auth/src/methods/requestMessage.ts | 20 ++++++- packages/auth/src/methods/verify.ts | 32 ++++++----- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/packages/auth/src/generated/types.ts b/packages/auth/src/generated/types.ts index 0a65906e63..6fcd922bfc 100644 --- a/packages/auth/src/generated/types.ts +++ b/packages/auth/src/generated/types.ts @@ -36,21 +36,21 @@ export interface components { * @enum {string} */ chainId: - | "ETH" - | "ROPSTEN" - | "RINKEBY" - | "GOERLI" - | "CRONOS" - | "KOVAN" - | "BSC" - | "BSC_TESTNET" - | "POLYGON" - | "FANTOM" - | "CRONOS_TESTNET" - | "LOCALDEVCHAIN" - | "AVALANCHE_TESTNET" - | "AVALANCHE" - | "MUMBAI"; + | "1" + | "3" + | "4" + | "5" + | "25" + | "42" + | "56" + | "97" + | "137" + | "250" + | "338" + | "1337" + | "43113" + | "43114" + | "80001"; /** * @description Ethereum address performing the signing conformant to capitalization encoded checksum specified in EIP-55 where applicable. * @example 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B @@ -148,21 +148,21 @@ export interface components { * @enum {string} */ chainId: - | "ETH" - | "ROPSTEN" - | "RINKEBY" - | "GOERLI" - | "CRONOS" - | "KOVAN" - | "BSC" - | "BSC_TESTNET" - | "POLYGON" - | "FANTOM" - | "CRONOS_TESTNET" - | "LOCALDEVCHAIN" - | "AVALANCHE_TESTNET" - | "AVALANCHE" - | "MUMBAI"; + | "1" + | "3" + | "4" + | "5" + | "25" + | "42" + | "56" + | "97" + | "137" + | "250" + | "338" + | "1337" + | "43113" + | "43114" + | "80001"; /** * @description Ethereum address performing the signing conformant to capitalization encoded checksum specified in EIP-55 where applicable. * @example 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B diff --git a/packages/auth/src/methods/requestMessage.ts b/packages/auth/src/methods/requestMessage.ts index 1268ec6128..0796033b6a 100644 --- a/packages/auth/src/methods/requestMessage.ts +++ b/packages/auth/src/methods/requestMessage.ts @@ -42,6 +42,24 @@ export interface RequestMessageSolOptions { timeout: number; } +// Temporary solution to allow allow chain id enum generated from swagger +type ChainID = + | '1' + | '3' + | '4' + | '5' + | '25' + | '42' + | '56' + | '97' + | '137' + | '250' + | '338' + | '1337' + | '43113' + | '43114' + | '80001'; + export type RequestMessageOptions = RequestMessageEvmOptions | RequestMessageSolOptions; const makeEvmRequestMessage = ( @@ -50,7 +68,7 @@ const makeEvmRequestMessage = ( ) => { return EndpointResolver.create(core, BASE_URL, initializeChallengeEvm).fetch({ // TODO: remove this when the API is fixed - chainId: EvmChain.create(chain).decimal as any, + chainId: EvmChain.create(chain).decimal.toString() as ChainID, address: EvmAddress.create(address).checksum, ...options, }); diff --git a/packages/auth/src/methods/verify.ts b/packages/auth/src/methods/verify.ts index c403dee509..e62b71dc17 100644 --- a/packages/auth/src/methods/verify.ts +++ b/packages/auth/src/methods/verify.ts @@ -32,20 +32,18 @@ const makeSolVerify = (core: MoralisCore, { network, ...options }: VerifySolOpti }); }; -export const makeVerify = - (core: MoralisCore) => - (options: VerifyOptions) => { - switch (options.network) { - case AuthNetwork.EVM: - return makeEvmVerify(core, options); - case AuthNetwork.SOLANA: - return makeSolVerify(core, options); - default: - throw new MoralisAuthError({ - code: AuthErrorCode.INCORRECT_NETWORK, - message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) - .map((value) => `"${value}"`) - .join(', ')}`, - }); - } - }; +export const makeVerify = (core: MoralisCore) => (options: VerifyOptions) => { + switch (options.network) { + case AuthNetwork.EVM: + return makeEvmVerify(core, options); + case AuthNetwork.SOLANA: + return makeSolVerify(core, options); + default: + throw new MoralisAuthError({ + code: AuthErrorCode.INCORRECT_NETWORK, + message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) + .map((value) => `"${value}"`) + .join(', ')}`, + }); + } +}; From f8b114f13bd3f666b702e52299410b155e674b96 Mon Sep 17 00:00:00 2001 From: Shalom Ogunshola Date: Wed, 7 Sep 2022 00:21:33 +0100 Subject: [PATCH 3/6] fix typescript for separate network --- .changeset/poor-fans-talk.md | 2 +- .../src/auth/MoralisAuthAdapter.ts | 4 +- packages/auth/src/MoralisAuth.ts | 17 +++++++- packages/auth/src/methods/requestMessage.ts | 20 +-------- packages/auth/src/methods/verify.ts | 41 +++++++++++-------- .../auth/src/resolvers/evmVerifyChallenge.ts | 1 - .../auth/src/resolvers/solVerifyChallenge.ts | 1 - .../src/dataTypes/EvmChain/EvmChain.ts | 24 +++++++++++ 8 files changed, 66 insertions(+), 44 deletions(-) diff --git a/.changeset/poor-fans-talk.md b/.changeset/poor-fans-talk.md index 6f185b1c0a..45b8d2659a 100644 --- a/.changeset/poor-fans-talk.md +++ b/.changeset/poor-fans-talk.md @@ -1,5 +1,5 @@ --- -'@moralisweb3/auth': major +'@moralisweb3/auth': minor --- Added solana authentication to auth package diff --git a/demos/parse-server/src/auth/MoralisAuthAdapter.ts b/demos/parse-server/src/auth/MoralisAuthAdapter.ts index f95c5211c5..75c1a33e68 100644 --- a/demos/parse-server/src/auth/MoralisAuthAdapter.ts +++ b/demos/parse-server/src/auth/MoralisAuthAdapter.ts @@ -15,9 +15,9 @@ function validateAuthData(authData: any) { const data = result.toJSON(); if (id === data.profileId && authId === data.id) { - authData.chainId = result.result.chain?.decimal; + authData.chainId = result.result.chain.decimal; authData.nonce = data.nonce; - authData.address = result.result.address.format(); + authData.address = result.result.address.checksum; authData.version = data.version; authData.domain = data.domain; authData.expirationTime = data.expirationTime; diff --git a/packages/auth/src/MoralisAuth.ts b/packages/auth/src/MoralisAuth.ts index d55095cbc6..d07ce362f4 100644 --- a/packages/auth/src/MoralisAuth.ts +++ b/packages/auth/src/MoralisAuth.ts @@ -1,6 +1,13 @@ import { ApiModule, MoralisCore, MoralisCoreProvider } from '@moralisweb3/core'; import { makeRequestMessage, RequestMessageOptions } from './methods/requestMessage'; -import { makeVerify, VerifyOptions } from './methods/verify'; +import { + makeVerify, + VerifyEvmData, + VerifyEvmOptions, + VerifyOptions, + VerifySolData, + VerifySolOptions, +} from './methods/verify'; export const BASE_URL = 'https://auth-api.do-prod-1.moralis.io'; @@ -24,5 +31,11 @@ export class MoralisAuth extends ApiModule { } public requestMessage = (options: RequestMessageOptions) => makeRequestMessage(this.core)(options); - public verify = (options: VerifyOptions) => makeVerify(this.core)(options); + + // Function overloading to make typescript happy + public verify(options: VerifyEvmOptions): VerifyEvmData; + public verify(options: VerifySolOptions): VerifySolData; + public verify(options: VerifyOptions) { + return makeVerify(this.core)(options); + } } diff --git a/packages/auth/src/methods/requestMessage.ts b/packages/auth/src/methods/requestMessage.ts index 0796033b6a..4a02e1048e 100644 --- a/packages/auth/src/methods/requestMessage.ts +++ b/packages/auth/src/methods/requestMessage.ts @@ -42,24 +42,6 @@ export interface RequestMessageSolOptions { timeout: number; } -// Temporary solution to allow allow chain id enum generated from swagger -type ChainID = - | '1' - | '3' - | '4' - | '5' - | '25' - | '42' - | '56' - | '97' - | '137' - | '250' - | '338' - | '1337' - | '43113' - | '43114' - | '80001'; - export type RequestMessageOptions = RequestMessageEvmOptions | RequestMessageSolOptions; const makeEvmRequestMessage = ( @@ -68,7 +50,7 @@ const makeEvmRequestMessage = ( ) => { return EndpointResolver.create(core, BASE_URL, initializeChallengeEvm).fetch({ // TODO: remove this when the API is fixed - chainId: EvmChain.create(chain).decimal.toString() as ChainID, + chainId: EvmChain.create(chain).apiId, address: EvmAddress.create(address).checksum, ...options, }); diff --git a/packages/auth/src/methods/verify.ts b/packages/auth/src/methods/verify.ts index e62b71dc17..872c907a18 100644 --- a/packages/auth/src/methods/verify.ts +++ b/packages/auth/src/methods/verify.ts @@ -4,7 +4,7 @@ import { BASE_URL } from '../MoralisAuth'; import { completeChallengeEvm, completeChallengeSol } from '../resolvers'; import { AuthNetwork } from './requestMessage'; -export interface VerifEvmOptions { +export interface VerifyEvmOptions { message: string; signature: string; network: 'evm'; @@ -16,9 +16,12 @@ export interface VerifySolOptions { network: 'solana'; } -export type VerifyOptions = VerifEvmOptions | VerifySolOptions; +export type VerifyOptions = VerifyEvmOptions | VerifySolOptions; -const makeEvmVerify = (core: MoralisCore, { network, ...options }: VerifEvmOptions) => { +export type VerifyEvmData = ReturnType; +export type VerifySolData = ReturnType; + +const makeEvmVerify = (core: MoralisCore, { network, ...options }: VerifyEvmOptions) => { return EndpointResolver.create(core, BASE_URL, completeChallengeEvm).fetch({ message: options.message, signature: options.signature, @@ -32,18 +35,20 @@ const makeSolVerify = (core: MoralisCore, { network, ...options }: VerifySolOpti }); }; -export const makeVerify = (core: MoralisCore) => (options: VerifyOptions) => { - switch (options.network) { - case AuthNetwork.EVM: - return makeEvmVerify(core, options); - case AuthNetwork.SOLANA: - return makeSolVerify(core, options); - default: - throw new MoralisAuthError({ - code: AuthErrorCode.INCORRECT_NETWORK, - message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) - .map((value) => `"${value}"`) - .join(', ')}`, - }); - } -}; +export const makeVerify = + (core: MoralisCore) => + (options: VerifyOptions) => { + switch (options.network) { + case AuthNetwork.EVM: + return makeEvmVerify(core, options); + case AuthNetwork.SOLANA: + return makeSolVerify(core, options); + default: + throw new MoralisAuthError({ + code: AuthErrorCode.INCORRECT_NETWORK, + message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) + .map((value) => `"${value}"`) + .join(', ')}`, + }); + } + }; diff --git a/packages/auth/src/resolvers/evmVerifyChallenge.ts b/packages/auth/src/resolvers/evmVerifyChallenge.ts index 2e84e3d483..8b073a0902 100644 --- a/packages/auth/src/resolvers/evmVerifyChallenge.ts +++ b/packages/auth/src/resolvers/evmVerifyChallenge.ts @@ -22,7 +22,6 @@ export const completeChallengeEvm = createEndpointFactory(() => ...data, // TODO: revisit EVM logic once we know how authentication in other networks work chain: EvmChain.create(chainId), - solNetwork: undefined, address: EvmAddress.create(data.address), expirationTime: maybe(data.expirationTime, (value) => new Date(value)), }), diff --git a/packages/auth/src/resolvers/solVerifyChallenge.ts b/packages/auth/src/resolvers/solVerifyChallenge.ts index eadf1f4fad..86e065b791 100644 --- a/packages/auth/src/resolvers/solVerifyChallenge.ts +++ b/packages/auth/src/resolvers/solVerifyChallenge.ts @@ -21,7 +21,6 @@ export const completeChallengeSol = createEndpointFactory(() => apiToResult: ({ network, ...data }: ApiResult) => ({ ...data, solNetwork: SolNetwork.create(network), - chain: undefined, address: SolAddress.create(data.address), expirationTime: maybe(data.expirationTime, (value) => new Date(value)), }), diff --git a/packages/evmUtils/src/dataTypes/EvmChain/EvmChain.ts b/packages/evmUtils/src/dataTypes/EvmChain/EvmChain.ts index a09e6ce13c..3945701d9e 100644 --- a/packages/evmUtils/src/dataTypes/EvmChain/EvmChain.ts +++ b/packages/evmUtils/src/dataTypes/EvmChain/EvmChain.ts @@ -324,6 +324,30 @@ export class EvmChain implements MoralisData, EvmChainable { | '0x19'; } + /** + * Validate and cast to api compatible id + * + * @example chain.apiId // 1 + */ + get apiId() { + return this._value as + | '1' + | '3' + | '4' + | '5' + | '25' + | '42' + | '56' + | '97' + | '137' + | '250' + | '338' + | '1337' + | '43113' + | '43114' + | '80001'; + } + /** * Returns the name of the chain * @example chain.name // "Ethereum" From 2b328b33333f33fc2ae24d50b31eb354f680fc18 Mon Sep 17 00:00:00 2001 From: Shalom Ogunshola Date: Wed, 7 Sep 2022 00:22:26 +0100 Subject: [PATCH 4/6] fix lint --- packages/auth/src/methods/verify.ts | 32 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/auth/src/methods/verify.ts b/packages/auth/src/methods/verify.ts index 872c907a18..5a0618a2db 100644 --- a/packages/auth/src/methods/verify.ts +++ b/packages/auth/src/methods/verify.ts @@ -35,20 +35,18 @@ const makeSolVerify = (core: MoralisCore, { network, ...options }: VerifySolOpti }); }; -export const makeVerify = - (core: MoralisCore) => - (options: VerifyOptions) => { - switch (options.network) { - case AuthNetwork.EVM: - return makeEvmVerify(core, options); - case AuthNetwork.SOLANA: - return makeSolVerify(core, options); - default: - throw new MoralisAuthError({ - code: AuthErrorCode.INCORRECT_NETWORK, - message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) - .map((value) => `"${value}"`) - .join(', ')}`, - }); - } - }; +export const makeVerify = (core: MoralisCore) => (options: VerifyOptions) => { + switch (options.network) { + case AuthNetwork.EVM: + return makeEvmVerify(core, options); + case AuthNetwork.SOLANA: + return makeSolVerify(core, options); + default: + throw new MoralisAuthError({ + code: AuthErrorCode.INCORRECT_NETWORK, + message: `Incorrect network provided. Got "${options.network}", Valid values are: ${Object.values(AuthNetwork) + .map((value) => `"${value}"`) + .join(', ')}`, + }); + } +}; From 47ff454788bd0f68265756270ec7ef17e876ab3e Mon Sep 17 00:00:00 2001 From: Shalom Ogunshola Date: Thu, 8 Sep 2022 14:35:32 +0100 Subject: [PATCH 5/6] remove fixed todo comment --- packages/auth/src/methods/requestMessage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/auth/src/methods/requestMessage.ts b/packages/auth/src/methods/requestMessage.ts index 4a02e1048e..3869a487f3 100644 --- a/packages/auth/src/methods/requestMessage.ts +++ b/packages/auth/src/methods/requestMessage.ts @@ -49,7 +49,6 @@ const makeEvmRequestMessage = ( { chain, address, network, ...options }: RequestMessageEvmOptions, ) => { return EndpointResolver.create(core, BASE_URL, initializeChallengeEvm).fetch({ - // TODO: remove this when the API is fixed chainId: EvmChain.create(chain).apiId, address: EvmAddress.create(address).checksum, ...options, From 3438b92ea933589340717206cf0495f1a8474145 Mon Sep 17 00:00:00 2001 From: Shalom Ogunshola Date: Thu, 8 Sep 2022 14:43:08 +0100 Subject: [PATCH 6/6] add solUtil package in auth package --- packages/auth/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/auth/package.json b/packages/auth/package.json index af25fd8d7f..edab608695 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -30,6 +30,7 @@ "dependencies": { "@moralisweb3/api-utils": "^2.3.1", "@moralisweb3/core": "^2.3.1", - "@moralisweb3/evm-utils": "^2.3.1" + "@moralisweb3/evm-utils": "^2.3.1", + "@moralisweb3/sol-utils": "^2.3.1" } }