Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions packages/proton-browser-transport/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
SigningRequest,
} from '@proton/link'
import {Storage} from './storage'
import {generateReturnUrl, isMobile, parseErrorMessage} from './utils'
import {closeDuplicateReturnTab, generateReturnUrl, isMobile, parseErrorMessage} from './utils'
import {type BrowserTransportOptions, SkipToManual} from './types'
import GenerateQrCode from './qrcode'
import {WebRenderer} from '@proton/web-renderer'
Expand All @@ -22,6 +22,7 @@ export class BrowserTransport implements LinkTransport {
private requestStatus: boolean
private requestAccount: string
private walletType: string
private returnUrl?: string | (() => string)
private activeRequest?: SigningRequest
// eslint-disable-next-line no-unused-vars
private activeCancel?: (reason: string | Error) => void
Expand All @@ -33,8 +34,11 @@ export class BrowserTransport implements LinkTransport {
this.requestAccount = options.requestAccount || ''
this.walletType = options.walletType || 'proton'
this.storage = new Storage(options.storagePrefix || 'proton-link')
this.returnUrl = options.returnUrl
this.showingManual = false

closeDuplicateReturnTab()

if (options.ui) {
this.ui = options.ui
} else {
Expand All @@ -46,13 +50,20 @@ export class BrowserTransport implements LinkTransport {
this.ui?.showLoading()
}

private getReturnUrl(): string {
if (typeof this.returnUrl === 'function') {
return this.returnUrl()
}
return this.returnUrl || generateReturnUrl()
}

public onSessionRequest(
session: LinkSession,
request: SigningRequest,
cancel: (_reason: string | Error) => void
) {
if (session.metadata.sameDevice) {
request.setInfoKey('return_path', generateReturnUrl())
request.setInfoKey('return_path', this.getReturnUrl())
}

if (session.type === 'fallback') {
Expand Down Expand Up @@ -232,7 +243,7 @@ export class BrowserTransport implements LinkTransport {
}
) {
const sameDeviceRequest = request.clone()
const returnUrl = generateReturnUrl()
const returnUrl = this.getReturnUrl()
sameDeviceRequest.setInfoKey('same_device', true)
sameDeviceRequest.setInfoKey('return_path', returnUrl)

Expand Down
5 changes: 5 additions & 0 deletions packages/proton-browser-transport/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface BrowserTransportOptions {
walletType?: string
/** Local storage prefix, defaults to `proton-link`. */
storagePrefix?: string
/**
* Return url (or factory) set as `return_path` on same-device requests,
* overriding the per-user-agent default from `generateReturnUrl()`.
*/
returnUrl?: string | (() => string)

ui?: UIRenderer
}
26 changes: 23 additions & 3 deletions packages/proton-browser-transport/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ export function isAndroidWebView() {
}

export function isMobile() {
return typeof window.orientation !== 'undefined' || navigator.userAgent.indexOf('IEMobile') !== -1
// window.orientation is removed in modern Chrome for Android, rely on UA-based checks
return isAndroid() || isAppleHandheld() || navigator.userAgent.indexOf('IEMobile') !== -1
}

/** Generate a return url that Proton will redirect back to w/o reload. */
Expand Down Expand Up @@ -100,13 +101,32 @@ export function generateReturnUrl() {
return 'android-app://webview'
}

if (isAndroid() && isChromeMobile()) {
return 'android-app://com.android.chrome'
// stock Chrome on Android: a bare `android-app://com.android.chrome` intent relaunches
// Chrome on a new tab instead of returning to the originating one, orphaning the pending
// request — fall through to the current URL so the flow completes on the dApp's origin
if (isAndroid()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SuperstrongBE Does this cover the case when user works in browser different from Chrome? Firefox for example. Previous check had isChromeMobile

// the wallet's return navigation always opens a NEW Chrome tab (Android cannot focus
// the originating one); tag the URL so the duplicate tab can close itself on load and
// drop the user back on the original tab (see closeDuplicateReturnTab)
return window.location.href.split('#')[0] + RETURN_TAB_FRAGMENT
}

return window.location.href
}

export const RETURN_TAB_FRAGMENT = '#webauth-return'

/**
* Close this tab if it was spawned by the wallet's return redirect on Android.
* Tabs opened by an external app intent are allowed to close themselves, which
* lands the user back on the originating tab where the pending request completes.
*/
export function closeDuplicateReturnTab() {
if (isAndroid() && window.location.hash.startsWith(RETURN_TAB_FRAGMENT)) {
window.close()
}
}

export function parseErrorMessage(error: any) {
let errorMessage: string

Expand Down