Skip to content

Commit

Permalink
🔄 synced local 'src/' with remote 'src/'
Browse files Browse the repository at this point in the history
  • Loading branch information
circle-github-action-bot committed Mar 1, 2024
1 parent 15e8425 commit 2e218f1
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 69 deletions.
169 changes: 114 additions & 55 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,13 @@ import type {
AppSettings,
Authentication,
Challenge,
ChallengeResult,
ChallengeStatus,
ChallengeType,
ChallengeCompleteCallback,
CustomLinks,
Error,
Localizations,
PostMessageEvent,
Resources,
SecurityQuestion,
SignMessageResult,
SsoSettings,
ThemeColor,
} from './types'

Expand All @@ -44,20 +41,25 @@ export class W3SSdk {
private themeColor?: ThemeColor
private resources?: Resources
private customLinks?: CustomLinks
private ssoSettings?: SsoSettings
/**
* Callback function that is called when the challenge is completed.
*/
private onComplete?: (
error: Error | undefined,
result: ChallengeResult | undefined
) => Promise<void> | void

private onComplete?: ChallengeCompleteCallback
private shouldCloseModalOnForgotPin = false
/**
* Callback function that is called when the user clicks the forgot pin button.
*/
private onForgotPin?: () => void
private receivedResponseFromService = false
/**
* Promise that is resolved when the device ID is received.
*/
private resolveDeviceIdPromise?: (deviceId: string) => void
/**
* Promise that is rejected when the device ID is not received.
*/
private rejectDeviceIdPromise?: (reason: string) => void

constructor() {
if (W3SSdk.instance != null) {
Expand All @@ -76,46 +78,48 @@ export class W3SSdk {
* @param challengeId - Challenge ID.
* @param onCompleted - Callback function that is called when the challenge is completed.
*/
execute(
challengeId: string,
onCompleted?: (
error: Error | undefined,
result: ChallengeResult | undefined
) => Promise<void> | void
): void {
execute(challengeId: string, onCompleted?: ChallengeCompleteCallback): void {
this.subscribeMessage()
this.setChallenge({ challengeId })
this.exec(onCompleted)
}

const protocol = this.window.location.protocol
const host = this.window.location.host
const fullDomainWithProtocol = `${protocol}//${host}`

this.iframe.src = `${this.serviceUrl}/?origin=${fullDomainWithProtocol}`
this.iframe.id = 'sdkIframe'
this.iframe.width = '100%'
this.iframe.height = '100%'

this.iframe.style.position = 'fixed'
this.iframe.style.top = '50%'
this.iframe.style.left = '50%'
this.iframe.style.transform = 'translate(-50%, -50%)'
this.iframe.style.zIndex = '2147483647'

document.body.appendChild(this.iframe)

this.onComplete = onCompleted
/**
* Executes the challenge with userSecret. This is used for SSO challenges.
* @param challengeId - Challenge ID.
* @param userSecret - User secret.
* @param onCompleted - Callback function that is called when the challenge is completed.
*/
executeWithKeyShare(
challengeId: string,
userSecret: string,
onCompleted?: ChallengeCompleteCallback
) {
this.subscribeMessage()
this.setChallenge({ challengeId, userSecret })
this.exec(onCompleted, !this.ssoSettings?.disableConfirmationUI)
}

setTimeout(() => {
if (!this.receivedResponseFromService) {
void this.onComplete?.(
{
code: 155706,
message: 'Network error',
},
undefined
)
}
}, 1000 * 10)
/**
* Gets the device ID.
* @returns Promise<string> - Device ID.
*/
getDeviceId(): Promise<string> {
return new Promise<string>((resolve, reject) => {
this.resolveDeviceIdPromise = resolve
this.rejectDeviceIdPromise = reject

this.subscribeMessage()
this.appendIframe(false, 'device-id')

setTimeout(() => {
if (!this.receivedResponseFromService) {
this.rejectDeviceIdPromise?.('Failed to receive deviceId')
this.closeModal()
this.unSubscribeMessage()
}
}, 1000 * 10)
})
}

/**
Expand Down Expand Up @@ -187,6 +191,14 @@ export class W3SSdk {
this.customLinks = customLinks
}

/**
* Sets the SSO settings.
* @param ssoSettings - SSO settings.
*/
setSsoSettings(ssoSettings: SsoSettings): void {
this.ssoSettings = ssoSettings
}

/**
* Sets the callback function that is called when the user clicks the forgot pin button.
* @param onForgotPin - Callback function that is called when the user clicks the forgot pin button.
Expand Down Expand Up @@ -215,6 +227,56 @@ export class W3SSdk {
this.challenge = challenge
}

/**
* Appends the iframe to the document body.
* @param showIframe - Indicates whether the iframe should be shown. Default is true.
* @param subRoute - Sub route.
*/
private appendIframe(showIframe = true, subRoute = '') {
const protocol = this.window.location.protocol
const host = this.window.location.host
const fullDomainWithProtocol = `${protocol}//${host}`

this.iframe.src = `${this.serviceUrl}/${subRoute}?origin=${fullDomainWithProtocol}`
this.iframe.id = 'sdkIframe'
this.iframe.width = showIframe ? '100%' : '0%'
this.iframe.height = showIframe ? '100%' : '0%'
this.iframe.style.zIndex = showIframe ? '2147483647' : '-1'
this.iframe.style.display = 'none'

if (showIframe) {
this.iframe.style.position = 'fixed'
this.iframe.style.top = '50%'
this.iframe.style.left = '50%'
this.iframe.style.transform = 'translate(-50%, -50%)'
this.iframe.style.display = ''
}

document.body.appendChild(this.iframe)
}

/**
* Executes the challenge.
* @param onCompleted - Callback function that is called when the challenge is completed.
* @param showIframe - Indicates whether the iframe should be shown. Default is true.
*/
private exec(onCompleted?: ChallengeCompleteCallback, showIframe = true) {
this.appendIframe(showIframe)
this.onComplete = onCompleted

setTimeout(() => {
if (!this.receivedResponseFromService) {
void this.onComplete?.(
{
code: 155706,
message: 'Network error',
},
undefined
)
}
}, 1000 * 10)
}

/**
* Handles the postMessage event.
* @param event - PostMessageEvent.
Expand Down Expand Up @@ -245,6 +307,7 @@ export class W3SSdk {
localizations: this.localizations,
resources: this.resources,
customLinks: this.customLinks,
ssoSettings: this.ssoSettings,
},
},
},
Expand All @@ -258,16 +321,12 @@ export class W3SSdk {
) as HTMLIFrameElement
iframe?.parentNode?.removeChild(iframe)

const result: ChallengeResult | undefined =
event.data?.result != null
? {
type: event.data?.result.type as ChallengeType,
status: event.data?.result.status as ChallengeStatus,
data: event.data?.result.data as SignMessageResult,
}
: undefined
void this.onComplete?.(undefined, event.data?.result)
} else if (event.data?.deviceId) {
this.resolveDeviceIdPromise?.(event.data.deviceId)

void this.onComplete?.(undefined, result)
this.closeModal()
this.unSubscribeMessage()
} else if (event.data?.onError) {
void this.onComplete?.(event.data?.error, undefined)
} else if (event.data?.onClose) {
Expand Down
83 changes: 69 additions & 14 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export enum ChallengeType {
CANCEL_TRANSACTION = 'CANCEL_TRANSACTION',
SIGN_MESSAGE = 'SIGN_MESSAGE',
SIGN_TYPEDDATA = 'SIGN_TYPEDDATA',
SIGN_TRANSACTION = 'SIGN_TRANSACTION',
UNKNOWN = 'UNKNOWN',
}

Expand Down Expand Up @@ -125,6 +126,7 @@ export enum ErrorCode {
biometricsUserLockoutPermanent = 155714,
biometricsUserNotAllowPermission = 155715,
biometricsInternalError = 155716,
invalidUserSecret= 155718,
walletIdNotFound = 156001,
tokenIdNotFound = 156002,
transactionIdNotFound = 156003,
Expand Down Expand Up @@ -157,22 +159,64 @@ export interface Challenge {
* Challenge ID.
*/
challengeId: string
/**
* SSO user secret.
*/
userSecret?: string
}

/**
* Base challenge result interface. Holds the challenge type and status.
*/
export interface ChallengeResult {
/**
* Challenge type.
*/
type: ChallengeType
/**
* Challenge status.
*/
status: ChallengeStatus
}

/**
* Result for sign message and sign typed-data challenges.
*/
export interface SignMessageResult {
export interface SignMessageResult extends ChallengeResult {
/**
* Signature.
* Challenge type.
*/
signature: string
type: ChallengeType.SIGN_MESSAGE | ChallengeType.SIGN_TYPEDDATA
data?: {
/**
* Signature.
*/
signature: string
}
}

export interface ChallengeResult {
type: ChallengeType
status: ChallengeStatus
data?: SignMessageResult
/**
* Result for sign transaction challenge.
*/
export interface SignTransactionResult extends ChallengeResult {
/**
* Challenge type.
*/
type: ChallengeType.SIGN_TRANSACTION
data?: {
/**
* Signature.
*/
signature: string
/**
* Transaction hash.
*/
txHash: string
/**
* Signed transaction.
*/
signedTransaction: string
}
}

export interface SecurityQuestion {
Expand All @@ -198,6 +242,15 @@ export interface Error {
message: string
}

export type ChallengeCompleteCallback = (
error: Error | undefined,
result:
| ChallengeResult
| SignMessageResult
| SignTransactionResult
| undefined
) => Promise<void> | void

export interface PostMessageEvent extends MessageEvent {
data: {
onFrameReady?: boolean
Expand All @@ -206,14 +259,9 @@ export interface PostMessageEvent extends MessageEvent {
onLearnMore?: boolean
onError?: boolean
onClose?: boolean
deviceId?: string
error?: Error
result?: {
type: string
status: string
data?: {
signature: string
}
}
result?: ChallengeResult | SignMessageResult | SignTransactionResult
}
}

Expand Down Expand Up @@ -523,3 +571,10 @@ export interface CustomLinks {
*/
learnMoreUrl?: string
}

export interface SsoSettings {
/**
* Controls whether the SSO UI is disabled.
*/
disableConfirmationUI?: boolean
}

0 comments on commit 2e218f1

Please sign in to comment.