Skip to content

Instantly share code, notes, and snippets.

@Carlgo11
Created April 23, 2024 16:41
Show Gist options
  • Save Carlgo11/1f1dca56947c0e701b3519d2ae6df3a0 to your computer and use it in GitHub Desktop.
Save Carlgo11/1f1dca56947c0e701b3519d2ae6df3a0 to your computer and use it in GitHub Desktop.
(function() {
'use strict';
const e = e => {
throw new Error('Unexpected value.');
}, t = () => globalThis.crypto.getRandomValues(
new Uint32Array(1))[0].toString(36), n = () => {
let e;
return {
resolve: t => e(t), promise: new Promise((t => {
e = t;
})),
};
};
var i, o;
!function(e) {
e.UpdateSettings = 'update-settings';
}(i || (i = {})), function(e) {
e.Create = 'create-credential', e.Get = 'get-credential', e.ShouldInterceptWebauthn = 'should-intercept-webauthn';
}(o || (o = {}));
const r = e => {
if (!e) return d();
if (e.aborted) return Promise.resolve({type: 'aborted'});
const t = n(), i = () => {
t.resolve({type: 'aborted'}), e.removeEventListener('abort', i);
};
return e.addEventListener('abort', i), t.promise;
}, s = e => 'string' == typeof e && 0 !== e.length,
a = e => e instanceof ArrayBuffer || e instanceof window.ArrayBuffer ||
e instanceof Uint8Array || e instanceof window.Uint8Array || e instanceof
DataView || e instanceof window.DataView || e instanceof Array ||
e instanceof window.Array ?
Array.from(
e instanceof ArrayBuffer || e instanceof window.ArrayBuffer ?
new Uint8Array(e):
e):
e, d = () => new Promise((() => {
}));
function c() {
return 'function' == typeof globalThis.cloneInto ?
globalThis.cloneInto:
void 0;
}
function l(e) {
const t = c();
return t ? t(e, window, {cloneFunctions: !0, wrapReflectors: !0}):e;
}
function u(e) {
const t = c();
return t ? new window.Promise(((n, i) => {
e.then((e => {
n(t(e, window, {cloneFunctions: !0, wrapReflectors: !0}));
})).catch((e => {
i(e);
}));
})):e;
}
class p {
constructor(e) {
this.send = async (e, {body: n, signal: i}) => {
const o = t(), r = await this.handleOutgoingSyn(o, e, i);
if ('ack' !== r.type) return r;
const s = r.data;
return (await this.handleOutgoingDirect(s.ackedById, o, e, n, i)).data;
}, this.getRoutes = () => Object.keys(
this.routes), this.handleOutgoingSyn = async (e, t, i) => {
const o = r(i), s = {
msgId: e,
source: this.source,
type: p.opSynType,
message: void 0,
name: t,
};
this.log.debug('starting request sequence', t);
const a = n();
this.synRequests[e] = a, window.postMessage(s), this.log.debug(
'starting ack race', t);
const d = await Promise.race([
a.promise, (c = 1e3, new Promise((e => {
setTimeout((() => e({type: 'timeout'})), c);
}))), o]);
var c;
if ('timeout' === d.type) return this.log.debug(
'finished request sequence: ack timed out',
t), delete this.synRequests[e], {type: p.NoReceivingEnd};
if ('aborted' === d.type ||
(null == i ? void 0:i.aborted)) return this.log.debug(
'finished request sequence: aborted sync',
t), delete this.synRequests[e], this.throwAbort(i);
this.log.debug(`ack race won by: ${d.data.ackedById}`, t);
const l = n();
return this.directRequests[e] = l, d;
}, this.handleIncomingSyn = async e => {
if (!this.routes[e.name]) return void this.log.debug(
'received a request with no handler to call', e.name);
const t = {
msgId: e.msgId,
source: this.source,
destination: e.source,
name: e.name,
type: p.opSynAckType,
message: void 0,
};
window.postMessage(t);
}, this.handleIncomingSynAck = e => {
if (void 0 === this.synRequests[e.msgId]) return;
this.synRequests[e.msgId].resolve({
type: 'ack',
data: {ackedById: e.source},
}), delete this.synRequests[e.msgId];
}, this.handleOutgoingDirect = async (e, t, i, o, s) => {
const a = r(s), d = n();
this.directRequests[t] = d;
const c = {
msgId: t,
source: this.source,
destination: e,
type: p.opDirectRequestType,
message: o,
name: i,
};
this.log.debug(`sending direct request to: ${e}`,
i), window.postMessage(c);
const l = await Promise.race([d.promise, a]);
if (delete this.directRequests[t], 'aborted' === l.type) {
const e = Object.assign(Object.assign({}, c), {type: p.opAbortType});
return e.reason = null == s ? void 0:s.reason, this.log.debug(
'notifying receiver to abort', i), window.postMessage(
e), this.log.debug('finished request sequence: aborted',
i), this.throwAbort(s);
}
return this.log.debug('finished request sequence: success', i), l;
}, this.handleIncomingDirectRequest = async e => {
const t = this.routes[e.name];
if (!t) return void this.log.error(
'received a request with no handler to call', e.name);
const n = new AbortController;
this.abortableRequests[e.msgId] = n;
const i = r(n.signal);
let o;
try {
o = await Promise.race([t({body: e.message, signal: n.signal}), i]);
} catch (e) {
const t = e instanceof Error ? e.message:'unknown';
o = {type: p.HandlerError, data: {reason: t}};
}
delete this.abortableRequests[e.msgId];
const s = o;
if (s && 'aborted' === (null == s ? void 0:s.type)) return;
const a = {
msgId: e.msgId,
source: this.source,
destination: e.source,
name: e.name,
type: p.opDirectResponseType,
message: o,
};
window.postMessage(a);
}, this.handleIncomingAbortRequest = e => {
if (void 0 ===
this.abortableRequests[e.msgId]) return void this.log.debug(
`received abort ${e.msgId} for no active request - likely already completed`,
e.name);
this.abortableRequests[e.msgId].abort(
e.reason), delete this.abortableRequests[e.msgId];
}, this.handleIncomingResponse = e => {
if (void 0 === this.directRequests[e.msgId]) return void this.log.debug(
`received response ${e.msgId} with no promise to resolve - likely already aborted`,
e.name);
this.directRequests[e.msgId].resolve({
type: 'completed',
data: e.message,
}), delete this.directRequests[e.msgId];
}, this.throwAbort = e => {
if (null == e ? void 0:e.reason) throw new DOMException(
null == e ? void 0:e.reason, 'AbortError');
throw new DOMException('signal is aborted without reason', 'AbortError');
}, this.validateMessage = e => {
const t = 'validate';
if ('object' != typeof e || null === e) return this.log.debug(
'invalid message - not an object', t), !1;
const n = e;
return s(n.msgId) ?
s(n.source) ?
s(n.name) ?
n.type === p.opSynType || n.type === p.opSynAckType ||
n.type === p.opAbortType || n.type ===
p.opDirectRequestType || n.type ===
p.opDirectResponseType ||
(this.log.debug('invalid message: type unsupported',
t), !1):
(this.log.debug('invalid message - missing name', t), !1):
(this.log.debug('invalid message - missing source', t), !1):
(this.log.debug('invalid message - missing msgId', t), !1);
}, this.routes = e, this.synRequests = {}, this.abortableRequests = {}, this.directRequests = {}, this.source = t(), this.debug = !1, this.log = {
error: (
e, t) => console.error(`PWM-${this.source}-${t}: ${e}`),
debug: (e, t) => {
this.debug && console.info(`PWM-${this.source}-${t}: ${e}`);
},
}, window.addEventListener('message', (e => {
if (!this.validateMessage(e.data)) return;
const t = e.data;
if (t.source === this.source) return;
if (t.destination && t.destination !== this.source) return;
const {type: n} = t;
n === p.opSynType && this.handleIncomingSyn(t), n === p.opSynAckType &&
this.handleIncomingSynAck(t), n === p.opAbortType &&
this.handleIncomingAbortRequest(t), n === p.opDirectRequestType &&
this.handleIncomingDirectRequest(t), n === p.opDirectResponseType &&
this.handleIncomingResponse(t);
}));
}
}
var g, w, y, h, b, v, f, m;
p.opSynType = 'op-window-syn', p.opSynAckType = 'op-window-syn-ack', p.opAbortType = 'op-window-abort', p.opDirectRequestType = 'op-window-direct-request', p.opDirectResponseType = 'op-window-direct-response', p.NoReceivingEnd = 'no-receiving-end', p.HandlerError = 'uncaught-handler-error';
const A = null === (w = null === (g = window.navigator) || void 0 === g ?
void 0:
g.credentials) || void 0 === w ?
void 0:
w.get.bind(window.navigator.credentials), R = null ===
(h = null === (y = window.navigator) || void 0 === y ?
void 0:
y.credentials) || void 0 === h ?
void 0:
h.create.bind(window.navigator.credentials), q = null ===
(v = null === (b = window.PublicKeyCredential) || void 0 === b ?
void 0:
b.isConditionalMediationAvailable) || void 0 === v ?
void 0:
v.bind(window.PublicKeyCredential), I = null ===
(m = null === (f = window.PublicKeyCredential) || void 0 === f ?
void 0:
f.isUserVerifyingPlatformAuthenticatorAvailable) || void 0 === m ?
void 0:
m.bind(window.PublicKeyCredential);
let T;
const O = new Promise((e => {
const t = () => {
T = !0, e(void 0);
};
window.document.prerendering ?
document.addEventListener('prerenderingchange', t):
t();
})), E = ({data: e}) => {
var t;
const n = {
clientDataJSON: new Uint8Array(e.response.clientDataJSON).buffer,
authenticatorData: new Uint8Array(e.response.authenticatorData).buffer,
signature: new Uint8Array(e.response.signature).buffer,
userHandle: e.response.userHandle ?
new Uint8Array(e.response.userHandle).buffer:
null,
};
Object.setPrototypeOf(n, AuthenticatorAssertionResponse.prototype);
const i = {
id: e.id,
rawId: new Uint8Array(e.rawId).buffer,
type: e.type,
authenticatorAttachment: null !== (t = e.authenticatorAttachment) &&
void 0 !== t ? t:null,
response: n,
getClientExtensionResults: () => {
var t;
return l(null !== (t = e.clientExtentionResults) && void 0 !== t ? t:{});
},
};
return Object.setPrototypeOf(i, PublicKeyCredential.prototype), i;
}, P = ({data: e}) => {
var t;
const n = {
attestationObject: new Uint8Array(e.response.attestationObject).buffer,
clientDataJSON: new Uint8Array(e.response.clientDataJSON).buffer,
getTransports: () => {
var t;
return null !== (t = e.response.transports) && void 0 !== t ? t:[];
},
getAuthenticatorData: () => {
var t;
return new Uint8Array(
null !== (t = e.response.authenticatorData) && void 0 !== t ?
t:
[]).buffer;
},
getPublicKey: () => e.response.publicKey ?
new Uint8Array(e.response.publicKey).buffer:
null,
getPublicKeyAlgorithm: () => e.response.publicKeyAlgorithm,
};
Object.setPrototypeOf(n, AuthenticatorAttestationResponse.prototype);
const i = {
id: e.id,
rawId: new Uint8Array(e.rawId).buffer,
type: e.type,
authenticatorAttachment: null !== (t = e.authenticatorAttachment) &&
void 0 !== t ? t:null,
response: n,
getClientExtensionResults: () => {
var t;
return l(null !== (t = e.clientExtentionResults) && void 0 !== t ? t:{});
},
};
return Object.setPrototypeOf(i, PublicKeyCredential.prototype), i;
};
let S;
const D = new p({
[i.UpdateSettings]: async e => {
void 0 !== e.body.authenticatePasskeys &&
(S = e.body.authenticatePasskeys);
},
}), k = async () => (void 0 === T && await O, void 0 === S &&
(S = await (async () => {
const e = {type: 'should-intercept-webauthn-request', data: void 0},
t = await D.send(o.ShouldInterceptWebauthn, {body: e});
return t.type !== p.NoReceivingEnd && t.type !== p.HandlerError &&
'should-intercept-webauthn-error' !== t.type && t.data;
})()), S);
async function U(t) {
if (!await k()) {
if (R) return R(t);
throw new DOMException(
'The operation either timed out or was not allowed. See https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.',
'NotAllowedError');
}
return (async t => {
const n = Object.assign({}, t);
null == n || delete n.signal;
const i = {
type: 'create-credential-request',
data: JSON.stringify(n, ((e, t) => a(t))),
}, r = await D.send(o.Create, {body: i});
if (r.type === p.NoReceivingEnd || r.type === p.HandlerError) return R(t);
if ('create-credential-error' === r.type) {
if ('timeout' === r.data.reason) throw new DOMException(
'The operation either timed out or was not allowed. See https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.',
'NotAllowedError');
if ('duplicate' === r.data.reason) throw new DOMException(
'The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.',
'InvalidStateError');
return 'user-cancelled' === r.data.reason ?
null:
('fallback-requested' === r.data.reason || 'internal-error' ===
r.data.reason || 'invalid-request' === r.data.reason ||
e(r.data.reason), R(t));
}
return P(r);
})(t);
}
async function M(t) {
if (!await k()) {
if (A) return A(t);
throw new DOMException(
'The operation either timed out or was not allowed. See https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.',
'NotAllowedError');
}
return (async t => {
var n;
const i = Object.assign({}, t);
null == i || delete i.signal, 'conditional' === i.mediation &&
(null === (n = i.publicKey) || void 0 === n ? void 0:n.timeout) &&
(i.publicKey.timeout = void 0);
const r = {
type: 'get-credential-request',
data: JSON.stringify(i, ((e, t) => a(t))),
}, s = await D.send(o.Get, {body: r});
if (s.type === p.NoReceivingEnd || s.type === p.HandlerError) return A(t);
if ('get-credential-error' === s.type) {
if ('timeout' === s.data.reason) throw new DOMException(
'The operation either timed out or was not allowed. See https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.',
'NotAllowedError');
if ('conditional' === i.mediation && 'user-cancelled' ===
s.data.reason) return d();
if ('conditional' === i.mediation && 'fallback-requested' ===
s.data.reason) {
const e = Object.assign({}, t);
return delete e.mediation, A(e);
}
return 'user-cancelled' === s.data.reason ?
null:
('fallback-requested' === s.data.reason || 'internal-error' ===
s.data.reason || 'invalid-request' === s.data.reason ||
e(s.data.reason), A(t));
}
return E(s);
})(t);
}
async function C() {
return !!await k() || !!q && q();
}
async function K() {
return !!await k() || !!I && I();
}
const j = () => {
const e = 'function' == typeof globalThis.exportFunction ?
globalThis.exportFunction:
void 0;
e ?
(e((e => u(U(e))), window.navigator.credentials,
{defineAs: 'create'}), e((e => u(M(e))),
window.navigator.credentials, {defineAs: 'get'}), e((() => u(C())),
window.PublicKeyCredential,
{defineAs: 'isConditionalMediationAvailable'}), e((() => u(K())),
window.PublicKeyCredential,
{defineAs: 'isUserVerifyingPlatformAuthenticatorAvailable'})):
(window.navigator.credentials.create = U, window.navigator.credentials.get = M, window.PublicKeyCredential.isConditionalMediationAvailable = C, window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable = K);
}, N = () => {
if (window.navigator.credentials.create.length > 0 &&
window.navigator.credentials.get.length > 0) return;
const e = window.wrappedJSObject ? window.wrappedJSObject:void 0;
e && e.navigator.credentials.create.length > 0 &&
e.navigator.credentials.get.length > 0 || j();
};
window.navigator.credentials && (k(), j(), setInterval(N, 100));
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment