@privacyportal.org/privacy-kit
Version:
Lightweight and FOSS browser library bringing Privacy Portal features to your website users
536 lines (535 loc) • 16.9 kB
JavaScript
/*
MIT License - Copyright (c) 2025 Privacy Portal.
See full license at https://github.com/privacyportal/privacy-kit/LICENSE.
*/
var W = Object.defineProperty;
var K = (e, t, n) => t in e ? W(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
var g = (e, t, n) => K(e, typeof t != "symbol" ? t + "" : t, n);
const G = { VITE_AUTHORIZATION_URL: "https://app.privacyportal.org/oauth/authorize", VITE_TOKEN_URL: "https://api.privacyportal.org/oauth/token" }, {
VITE_AUTHORIZATION_URL: w,
VITE_TOKEN_URL: J
} = G, L = {
SCOPE: ["openid", "email"],
RESPONSE_TYPE: "code",
RESPONSE_MODE: "web_message"
}, Y = "Please try again later.";
class l extends Error {
constructor({ message: t }) {
super(t);
}
}
function h(e) {
if (e instanceof l)
switch (d.onError) {
case "ignore":
break;
case "alert": {
alert(e.message);
break;
}
default:
X(d.onError) && d.onError.error(e.message);
}
}
function X(e) {
return e != null && typeof e == "object" && typeof e.error == "function";
}
class Q {
constructor() {
g(this, "_client_id");
g(this, "_name_scope_required");
g(this, "_onError");
}
get client_id() {
return this._client_id;
}
set client_id(t) {
this._client_id = t;
}
get onError() {
return this._onError;
}
set onError(t) {
this._onError = t;
}
get name_scope_required() {
return this._name_scope_required || !1;
}
set name_scope_required(t) {
this._name_scope_required = t;
}
get redirect_uri() {
return window.location.origin;
}
get authorization_origin() {
return new URL(w).origin;
}
get authorization_url_placeholder() {
return `${w}?loading`;
}
getNameScopeRequired(t) {
return (t == null ? void 0 : t.name_scope_required) !== void 0 ? t == null ? void 0 : t.name_scope_required : this.name_scope_required;
}
getScope(t) {
return [
...L.SCOPE,
...this.getNameScopeRequired(t) ? ["name"] : []
].join(" ");
}
createAuthorizationURL(t, n, i) {
if (!this.client_id)
throw new l({ message: "OAuth client_id not configured." });
const r = new URLSearchParams();
r.set("client_id", this.client_id), r.set("scope", this.getScope(i)), r.set("response_type", L.RESPONSE_TYPE), r.set("response_mode", L.RESPONSE_MODE), r.set("nonce", crypto.randomUUID().substring(4, 18)), r.set("redirect_uri", this.redirect_uri), r.set("state", t), r.set("code_challenge", n), r.set("code_challenge_method", "S256");
const o = new URL(w);
return o.search = r.toString(), o.toString();
}
}
const d = new Q();
function E(e, t) {
return (n) => (i) => {
var o;
const r = t != null && t.shadow ? (o = i == null ? void 0 : i.composedPath()) == null ? void 0 : o[0] : i == null ? void 0 : i.target;
if (r)
return r[t != null && t.closest ? "closest" : "matches"](e) && n(r);
};
}
function A(e, t) {
return e && t ? (e.value = t, e.dispatchEvent(new Event("paste", { bubbles: !0 })), e.dispatchEvent(new Event("change", { bubbles: !0 })), !0) : !1;
}
function S(e) {
return e.offsetWidth !== 0 || e.offsetHeight !== 0;
}
function R(e) {
return e.map((t) => `${t}:not(:disabled)`).join(", ");
}
function I(e, t) {
e.addEventListener("focusin", () => {
t.style.opacity = "1";
}), e.addEventListener("focusout", () => {
t.style.opacity = "0";
}), document.addEventListener(
"click",
() => {
t.style.opacity = document.activeElement === e ? "1" : "0";
},
!0
);
}
async function U(e) {
let t = e.parentElement, n;
for (; t; ) {
try {
n = t.shadowRoot || t.attachShadow({ mode: "open" });
const i = document.createElement("style");
i.textContent = ":host{position:relative;display:block;box-sizing:border-box;margin:0;padding:0;}";
const r = document.createElement("slot");
n.append(i, r);
} catch {
}
if (n) return { ancestor: t, shadowRoot: n };
t = t.parentElement;
}
throw new Error("Failed to attach HME DOM.");
}
function x(e, t, n) {
let i = 0;
const r = () => {
cancelAnimationFrame(i), i = requestAnimationFrame(n);
}, o = new ResizeObserver(r);
o.observe(e), o.observe(t);
const c = new FinalizationRegistry((s) => {
s.unobserve(e), s.unobserve(t), s.disconnect();
}), a = new WeakRef(e);
c.register(a, o);
}
function f(e, t) {
return (t != null && t.toFixed ? e.toFixed(t.toFixed) : e) + "px";
}
function m(e) {
const t = window.open(
d.authorization_url_placeholder,
"PrivacyPortalSSO",
"top=0"
);
t ? e(t) : h(
new l({
message: "Please allow popups in order to use Hide-my-Email."
})
);
}
function ee(e) {
const t = e.split(".", 3)[1];
return {
...JSON.parse(atob(t))
};
}
function te(e) {
const t = e instanceof Uint8Array ? e : new Uint8Array(e);
return btoa(String.fromCharCode(...t));
}
function j(e) {
return ne(te(e));
}
function ne(e) {
return e.replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, "");
}
function P(e) {
return typeof e == "string" || e instanceof String;
}
const q = [
"Authentication failed.",
Y
].join(" ");
function ie(e = 16) {
const t = new Uint8Array(e);
return crypto.getRandomValues(t), Array.from(
t,
(n) => n.toString(16).padStart(2, "0")
).join("");
}
function re(e = 32) {
const t = new Uint8Array(e);
return crypto.getRandomValues(t), j(t);
}
async function oe(e) {
const n = new TextEncoder().encode(e), i = await crypto.subtle.digest("SHA-256", n);
return j(i);
}
async function ae(e, t) {
try {
if (!e) throw new l({ message: "code missing." });
if (!d.client_id)
throw new l({ message: "OAuth client_id not configured" });
const n = new URLSearchParams();
return n.set("client_id", d.client_id), n.set("grant_type", "authorization_code"), n.set("code", e), n.set("redirect_uri", d.redirect_uri), n.set("code_verifier", t), (await fetch(J, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: n
})).json();
} catch (n) {
throw n instanceof l ? n : new l({ message: q });
}
}
async function ce({
id_token: e,
access_token: t
}) {
if (!e || !t || !P(e) || !P(t))
throw new l({ message: q });
return { id_token: e, access_token: t };
}
async function se(e, t) {
try {
const n = ie(), i = re(), r = await oe(i);
e.location.href = d.createAuthorizationURL(
n,
r,
t
);
let o, c;
return await Promise.race([
new Promise((a, s) => {
window.addEventListener(
"message",
async (_) => {
if (_.origin !== d.authorization_origin) return {};
clearTimeout(o), clearInterval(c);
const { code: V, state: Z } = _.data;
if (n !== Z)
return s(
new l({ message: "Authentication failed." })
);
const { id_token: O, access_token: T } = await ae(
V,
i
).catch(s);
await ce({ id_token: O, access_token: T }).catch(s), a({ id_token: O, access_token: T });
},
!1
);
}),
new Promise((a) => {
c = setInterval(() => {
e.closed && (clearTimeout(o), clearInterval(c), a(void 0));
}, 1e3);
}),
new Promise((a, s) => {
o = setTimeout(() => {
clearInterval(c), s(new l({ message: "Authorization timed out." }));
}, 18e4);
})
]);
} catch (n) {
h(n);
}
}
async function z(e, t) {
const n = await se(e, t);
if (n != null && n.id_token) {
const { email: i, name: r } = ee(n.id_token);
return { email: i, name: r };
}
return {};
}
function de(e, t, n) {
const i = document.createElementNS("http://www.w3.org/2000/svg", "svg");
e != null && e.width && i.setAttribute("width", e.width), e != null && e.height && i.setAttribute("height", e.height), e != null && e.viewBox && i.setAttribute("viewBox", e.viewBox), n != null && n.fillRule && (i.style.fillRule = n.fillRule), n != null && n.clipRule && (i.style.clipRule = n.clipRule), n != null && n.strokeLinejoin && (i.style.strokeLinejoin = n.strokeLinejoin), n != null && n.strokeMiterlimit && (i.style.strokeMiterlimit = n.strokeMiterlimit);
for (const r of t)
i.appendChild(r);
return i;
}
function N(e, t) {
const n = document.createElementNS("http://www.w3.org/2000/svg", "path");
return n.setAttribute("d", e), t != null && t.fill && (n.style.fill = t.fill), n;
}
function le() {
return de(
{ width: "100%", height: "100%", viewBox: "0 0 150 150" },
[
N(
"M150,22.5C150,10.082 139.918,0 127.5,0L22.5,0C10.082,0 0,10.082 0,22.5L0,127.5C0,139.918 10.082,150 22.5,150L127.5,150C139.918,150 150,139.918 150,127.5L150,22.5Z",
{ fill: "#333" }
),
N(
"M112.979,124.728C120.977,123.051 126.993,115.921 126.993,107.39C126.993,102.517 125.03,98.101 121.857,94.897L126.44,90.314L126.45,90.324C130.816,94.695 133.518,100.73 133.518,107.39C133.518,119.687 124.308,129.85 112.414,131.351L114.651,133.588L110.239,138L100.561,128.322L110.239,118.644L114.651,123.056L112.979,124.728ZM106.337,83.425L104.103,81.191L108.515,76.779L118.192,86.457L108.515,96.135L104.103,91.723L105.781,90.045C97.768,91.71 91.737,98.848 91.737,107.39C91.737,112.261 93.698,116.675 96.87,119.879L96.873,119.882L92.391,124.363L92.293,124.469L92.29,124.465C87.918,120.093 85.212,114.055 85.212,107.39C85.212,95.085 94.434,84.916 106.337,83.425ZM78.539,116.812L30.658,116.812C22.834,116.812 16.482,110.46 16.482,102.636L16.482,36.484C16.482,28.661 22.834,22.309 30.658,22.309L117.06,22.309C124.884,22.309 131.236,28.661 131.236,36.484L131.236,82.065C128.437,79.412 125.165,77.253 121.561,75.728L121.561,36.128C121.561,36.128 86.535,62.757 76.366,70.488C74.751,71.716 72.513,71.711 70.903,70.475C60.81,62.728 26.158,36.128 26.158,36.128L26.158,107.005L76.571,107.005C76.719,110.44 77.402,113.736 78.539,116.812ZM109.365,86.454L109.365,86.461L109.369,86.457L109.365,86.454ZM73.627,59.885L111.21,31.984L36.508,31.984L73.627,59.885Z",
{ fill: "#fff" }
)
],
{
fillRule: "evenodd",
clipRule: "evenodd",
strokeLinejoin: "round",
strokeMiterlimit: "2"
}
);
}
const H = "firefox", ue = "android", fe = "safari", he = "chrome", _e = "chromium", ge = "criOS";
function C(e, t) {
const n = e.userAgent.toLowerCase();
for (const i of t)
if (n.indexOf(i) === -1) return !1;
return !0;
}
function ye(e, t) {
const n = e.userAgent.toLowerCase();
for (const i of t)
if (n.indexOf(i) > -1) return !0;
return !1;
}
function me(e) {
return C(e, [H, ue]);
}
function we(e) {
return C(e, [H]);
}
function Le(e) {
return C(e, [fe]) && !ye(e, [
he,
_e,
ge
]);
}
let F;
const M = "*****@pportal.io", y = "Hide my Email", u = "input[type=text]", $ = [
"input[type=email]",
`${u}[id*="email" i]`,
`${u}[name*="email" i]`,
`${u}[name*="username" i]`,
`${u}[name*="login" i]`,
`${u}[placeholder*="email" i]`,
`${u}[placeholder*="e-mail" i]`
], p = $.map(
(e) => `${e}:not([data-pp])`
).join(", "), be = E(p), Ae = E(
p,
{ shadow: !0 }
);
async function b(e, t) {
e.value = "", F = e;
const { email: n } = await z(t);
A(F, n);
}
async function v(e) {
const t = `pp-${window.crypto.randomUUID().substring(0, 8)}`;
if (me(navigator) || Le(navigator)) {
const { ancestor: n, shadowRoot: i } = await U(e), r = document.createElement("li");
r.innerText = y, r.style.padding = "3px", r.style.cursor = "pointer";
const o = document.createElement("ul");
o.id = t, o.style.display = "block", o.style.position = "absolute", o.style.maxHeight = "300px", o.style.overflowY = "auto", o.style.listStyle = "none", o.style.backgroundColor = "Canvas", o.style.color = "CanvasText", o.style.colorScheme = "light dark", o.style.boxShadow = "0 2px 2px #999", o.style.fontSize = "small", o.style.zIndex = "1000", o.style.opacity = "0", o.style.padding = o.style.margin = "0px", o.appendChild(r);
const c = document.createElement("style");
c.textContent = "li:hover{background-color:ButtonFace;color:ButtonText;}", i.append(c, o), x(e, n, () => {
const a = e.getBoundingClientRect(), s = n.getBoundingClientRect();
o.style.width = f(a.width), o.style.top = f(a.bottom - s.top, {
toFixed: 2
}), o.style.left = f(a.left - s.left, {
toFixed: 2
});
}), e.setAttribute("data-pp", ""), r.onmousedown = (a) => {
a.preventDefault();
}, r.onclick = () => {
m((a) => {
o.style.opacity = "0", b(e, a).catch(h);
});
}, I(e, o);
} else {
const n = document.createElement("option");
n.setAttribute("id", "new-privacy-addr"), n.setAttribute("value", M), n.textContent = y;
let i;
if (e.hasAttribute("list")) {
const r = e.getAttribute("list");
r && (i = document.getElementById(r));
}
if (i || (i = document.createElement("datalist"), i.setAttribute("id", t), e.setAttribute("list", t), e.insertAdjacentElement("afterend", i)), i.appendChild(n), e.setAttribute("data-pp", ""), we(navigator)) {
const { ancestor: r, shadowRoot: o } = await U(e), c = document.createElement("button");
c.title = y, c.ariaLabel = y, Object.assign(c.style, {
position: "absolute",
border: "none",
borderRadius: "15px",
cursor: "pointer",
padding: "0",
margin: "0",
zIndex: "1000",
pointerEvents: "auto",
right: "8px",
opacity: "0"
}), c.appendChild(le()), o.append(c), x(e, r, () => {
const a = e.getBoundingClientRect(), s = r.getBoundingClientRect(), _ = parseFloat(
window.getComputedStyle(e).getPropertyValue("padding-right")
) || 0;
c.style.height = c.style.width = f(
a.height * 0.7,
{ toFixed: 2 }
), c.style.top = f(
a.top - s.top + a.height * 0.15,
{ toFixed: 2 }
), c.style.right = f(
s.right - a.right + Math.max(_, a.height * 0.15),
{ toFixed: 2 }
);
}), c.onmousedown = (a) => {
a.preventDefault();
}, c.onclick = () => {
m((a) => {
c.disabled = !0, b(e, a).catch(h).finally(() => {
c.disabled = !1;
});
});
}, I(e, c);
}
}
e.addEventListener("input", () => {
(e.value === "@" || e.value === M) && m((n) => {
b(e, n).catch(h);
});
});
}
function D() {
for (const e of Array.from(
document.querySelectorAll(p)
))
S(e) && v(e).catch(console.error);
}
function Se(e) {
e != null && e.addEventListener && e.addEventListener(
"focusin",
be((t) => {
v(t).catch(console.error);
}),
!0
);
}
function k() {
document.addEventListener(
"click",
Ae((e) => {
v(e).catch(console.error);
}),
!0
);
}
function B() {
[...Array.from(document.querySelectorAll("iframe"))].map((e) => {
var t;
try {
const n = (e == null ? void 0 : e.contentDocument) || ((t = e == null ? void 0 : e.contentWindow) == null ? void 0 : t.document);
n != null && n.addEventListener && Se(n);
} catch {
}
});
}
function Ee() {
window.addEventListener("load", () => {
D(), k(), B();
}), document.readyState === "complete" && (D(), k(), B());
}
const Re = [
"form button[data-pp-action=subscribe-anonymously]"
], Ce = R(
Re
), pe = R($), ve = R([
`${u}[id*="name" i]`,
`${u}[name*="name" i]`
]), Oe = E(
Ce,
{ closest: !0 }
);
async function Te(e, t) {
try {
const n = e.closest("form");
if (!n) return;
const i = [
...Array.from(
n.querySelectorAll(pe)
)
].find(S);
if (!i) return;
const r = [
...Array.from(
n.querySelectorAll(ve)
)
].find(S);
let o;
r != null && r.required && (o = { name_scope_required: !0 });
const { email: c, name: a } = await z(t, o);
A(i, c) && (!r || !a || A(r, a)) && n.dispatchEvent(new Event("submit", { cancelable: !0 })) && n.submit();
} catch (n) {
h(n);
}
}
function Ie(e) {
e != null && e.addEventListener && e.addEventListener(
"click",
Oe((t) => {
m((n) => {
Te(t, n);
});
}),
!0
);
}
function Ue() {
window.addEventListener("load", () => {
Ie(document);
});
}
function xe(e) {
return typeof e == "object" && e !== null;
}
const Pe = {
hide_my_email: !0,
subscribe_anonymously: !0,
on_error: "alert"
};
class Fe {
static run(t) {
const { client_id: n, hide_my_email: i, subscribe_anonymously: r, on_error: o } = {
...Pe,
...t
};
d.client_id = n, d.onError = o, i && Ee(), r && (xe(r) && (d.name_scope_required = r.set_name_field), Ue());
}
}
export {
Fe as default
};