|
5 | 5 |
|
6 | 6 | import type { BrowserWindow, Request } from 'electron' |
7 | 7 |
|
| 8 | +import { session } from 'electron' |
8 | 9 | import { showCertificateTrustDialog } from '../certificate/certificate.window.ts' |
9 | 10 | import { getAppConfig, setAppConfig } from './AppConfig.ts' |
10 | 11 |
|
@@ -52,3 +53,46 @@ export async function promptCertificateTrust(window: BrowserWindow, details: Unt |
52 | 53 |
|
53 | 54 | return isAccepted |
54 | 55 | } |
| 56 | + |
| 57 | +/** |
| 58 | + * Verify certificate on a URL. |
| 59 | + * Note: this function only exists due to Electron limitations. |
| 60 | + * If a user accepts the certificate later than the request is rejected by timeout, |
| 61 | + * Electron considers it rejected for 30 minutes or until the app restart. |
| 62 | + * Issue: https://github.com/electron/electron/issues/47267 |
| 63 | + * And there is no way to reset the cache. |
| 64 | + * Issue: https://github.com/electron/electron/issues/41448 |
| 65 | + * Thus the verification on the defaultSession cannot be used (at least for login). |
| 66 | + * This function makes a single request in a new random session, to avoid verification caching. |
| 67 | + * The actual result is stored in the application config. |
| 68 | + * |
| 69 | + * @param window - Parent browser window |
| 70 | + * @param url - URL |
| 71 | + */ |
| 72 | +export async function verifyCertificate(window: BrowserWindow, url: string): Promise<boolean> { |
| 73 | + const certificateVerifySession = session.fromPartition(`certificate:verify:${Math.random().toString(36).slice(2, 9)}`) |
| 74 | + |
| 75 | + let verificationResolvers: PromiseWithResolvers<boolean> | undefined |
| 76 | + |
| 77 | + certificateVerifySession.setCertificateVerifyProc(async (request, callback) => { |
| 78 | + verificationResolvers = Promise.withResolvers() |
| 79 | + // Use original result, failing the request |
| 80 | + callback(-3) |
| 81 | + |
| 82 | + const isAccepted = request.errorCode === 0 || await promptCertificateTrust(window, request) |
| 83 | + verificationResolvers.resolve(isAccepted) |
| 84 | + }) |
| 85 | + |
| 86 | + try { |
| 87 | + await certificateVerifySession.fetch(url, { bypassCustomProtocolHandlers: true }) |
| 88 | + // Successful request - no SSL errors |
| 89 | + return true |
| 90 | + } catch { |
| 91 | + // SSL Error - handled by user prompt |
| 92 | + if (verificationResolvers) { |
| 93 | + return verificationResolvers.promise |
| 94 | + } |
| 95 | + // Some unexpected network error - not a certificate error |
| 96 | + return true |
| 97 | + } |
| 98 | +} |
0 commit comments