diff --git a/CHANGELOG.md b/CHANGELOG.md index 009b373a..cb2b5f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,10 +19,12 @@ Feature release including multi-hook support, improved UX for off chain token ho - Multi-Hook support added - Added selected token issuer keys to 'tokens-selected' event hook - Explicitly include ethers library availabilty via Token Negotiator library interface `client.externalUtils.evm.ethers` +- Aligned on chain authentication with off chain user interface (for single token authentication on the client side). Multi token on chain authentication is not yet supported (via the current library features). ### Bug Fixes - TS interface custom view +- Single token off chain authentication incompatibility interface fix ### Performance / Quality Improvements diff --git a/README.md b/README.md index e4a51bfc..edec7f96 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ const negotiator = new Client({ ​​ negotiator.negotiate(); ​ ​ -negotiator.on("tokens-selected", (tokens) => { - console.log('owner tokens found: ', tokens); +negotiator.on("tokens-selected", ({ selectedTokens, selectedIssuerKeys }) => { + console.log('user selected tokens: ', selectedTokens); }); ​ ``` diff --git a/src/client/index.ts b/src/client/index.ts index 8ad3650d..b095e948 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -38,6 +38,7 @@ import { MultiTokenAuthRequest, MultiTokenAuthResult, OutletIssuerInterface, Pro import { AttestationIdClient } from '../outlet/attestationIdClient' import { EventHookHandler } from './eventHookHandler' import { ethers } from 'ethers' +import { TokenListItemInterface } from './views/token-list' if (typeof window !== 'undefined') window.tn = { VERSION } @@ -485,7 +486,7 @@ export class Client { private tokensSelectedCallBackHandler = () => { this.eventSender('tokens-selected', { selectedTokens: this.tokenStore.getSelectedTokens(), - selectedTokenKeys: Object.keys(this.tokenStore.getSelectedTokens()), + selectedIssuerKeys: Object.keys(this.tokenStore.getSelectedTokens()), }) } @@ -726,6 +727,7 @@ export class Client { tokens.map((token) => { token.walletAddress = walletAddress + token.collectionId = issuer.collectionID return token }) @@ -817,12 +819,15 @@ export class Client { async prepareToAuthenticateToken(authRequest: AuthenticateInterface) { await this.checkUserAgentSupport('authentication') - const { unsignedToken, tokenId } = authRequest - if (!authRequest.issuer) authRequest.issuer = unsignedToken?.collectionId - requiredParams(authRequest.issuer && (unsignedToken || tokenId), 'Issuer and unsigned token required.') - const config = this.tokenStore.getCurrentIssuers()[authRequest.issuer] + let unsignedToken = authRequest?.unsignedToken + if (!unsignedToken && authRequest.collectionId) unsignedToken = authRequest + const issuer = authRequest?.issuer ?? unsignedToken?.collectionId ?? authRequest?.collectionId + const tokenId = authRequest?.tokenId + console.log('tokenIssuer', issuer, 'unsignedToken', unsignedToken) + requiredParams(issuer && (unsignedToken || tokenId), 'Issuer and unsigned token required MUTLI.') + const config = this.tokenStore.getCurrentIssuers()[issuer] if (!config) errorHandler('Provided issuer was not found.', 'error', null, null, true, true) - return authRequest + return { unsignedToken, issuer, tokenId } } async getMultiRequestBatch(authRequests: AuthenticateInterface[]) { @@ -830,41 +835,29 @@ export class Client { onChain: {}, offChain: {}, } - // build a list of the batches for each token origin. At this point when this loop is complete - // we will have a list of all the tokens that need to be authenticated and the origin they need to be authenticated against. await Promise.all( authRequests.map(async (authRequestItem) => { const reqItem = await this.prepareToAuthenticateToken(authRequestItem) - - if (!reqItem.tokenId && reqItem.unsignedToken?.tokenId) reqItem.tokenId = reqItem.unsignedToken?.tokenId - const issuerConfig = this.tokenStore.getCurrentIssuers()[reqItem.issuer] as OffChainTokenConfig - // Off Chain - // Setup for Token Collection. e.g. authRequestBatch.offChain['https://mywebsite.com']['devcon'] - /** - * Always generate a batch - */ if (issuerConfig.onChain === false) { if (!authRequestBatch.offChain[issuerConfig.tokenOrigin]) authRequestBatch.offChain[issuerConfig.tokenOrigin] = {} - if (!authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer]) { authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer] = { tokenIds: [], issuerConfig: issuerConfig, } } - // Push token into the request batch - authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer].tokenIds.push(reqItem.tokenId) + authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer].tokenIds.push( + reqItem.tokenId ?? reqItem.unsignedToken.tokenId, + ) return } - throw new Error('On-chain token are not supported by batch authentication at this time.') }), ) - - if (Object.keys(authRequestBatch.offChain).length > 1) + if (Object.keys(authRequestBatch.offChain).length > 1) { throw new Error('Only a single token origin is supported by batch authentication at this time.') - + } return authRequestBatch } @@ -890,7 +883,6 @@ export class Client { const authRequestBatch = await this.getMultiRequestBatch(authRequests) let issuerProofs = {} - // Send the request batches to each token origin: // Off Chain: // ['https://devcon.com']['issuer'][list of tokenIds] for (const tokenOrigin in authRequestBatch.offChain) { let AuthType = TicketZKProofMulti @@ -933,13 +925,15 @@ export class Client { else return this.authenticateToken(authRequest as AuthenticateInterface) } - async authenticateToken(authRequest: AuthenticateInterface) { + async authenticateToken(authRequest: AuthenticateInterface | any) { await this.checkUserAgentSupport('authentication') - const { unsignedToken, issuer } = authRequest - const tokenIssuer = issuer ?? unsignedToken.collectionId + const tokenIssuer = authRequest.issuer ?? authRequest.unsignedToken?.collectionId ?? authRequest.collectionId + let unsignedToken = authRequest.unsignedToken + if (!unsignedToken && authRequest.collectionId) unsignedToken = authRequest - requiredParams(tokenIssuer && unsignedToken, 'Issuer and unsigned token required.') + console.log('tokenIssuer', tokenIssuer, 'unsignedToken', unsignedToken) + requiredParams(tokenIssuer && unsignedToken, 'Issuer and unsigned token required. SINGLE') const config = this.tokenStore.getCurrentIssuers()[tokenIssuer] @@ -1137,6 +1131,7 @@ export class Client { if (issuerConfig) this.storeOutletTokenResponse(res.data.tokens) this.eventSender('tokens-selected', { selectedTokens: this.tokenStore.getSelectedTokens(), + selectedIssuerKeys: Object.keys(this.tokenStore.getSelectedTokens()), }) return res.data.tokens } diff --git a/src/client/interface.ts b/src/client/interface.ts index 5bb34a80..a7a11bd4 100644 --- a/src/client/interface.ts +++ b/src/client/interface.ts @@ -34,6 +34,7 @@ export interface OnChainTokenConfig extends IssuerConfigInterface { openSeaSlug?: string blockchain?: SupportedBlockchainsParam oAuth2options?: any + abi?: string } export interface UltraIssuerConfig extends OnChainTokenConfig { @@ -143,6 +144,7 @@ export interface MultiTokenInterface { } export interface AuthenticateInterface { + collectionId?: string issuer: string tokenId?: number | string unsignedToken: any diff --git a/src/outlet/index.ts b/src/outlet/index.ts index 9cefca4e..d0a6a66a 100644 --- a/src/outlet/index.ts +++ b/src/outlet/index.ts @@ -209,13 +209,13 @@ export class Outlet extends LocalOutlet { async sendTokenProof(evtid: string) { const collectionId: string = this.getDataFromQuery('issuer') - const tokenId: string = this.getDataFromQuery('tokenId') + const token: string = this.getDataFromQuery('tn-token') const wallet: string = this.getDataFromQuery('wallet') const address: string = this.getDataFromQuery('address') + const tokenParsed = token ? JSON.parse(token) : undefined + const tokenId = tokenParsed.tokenId ?? this.getDataFromQuery('tokenId') requiredParams(tokenId, 'tokenId is missing') - const redirect = this.getDataFromQuery('redirect') === 'true' ? window.location.href : false - try { const issuer = this.getIssuerConfigById(collectionId)