/* global atob window document location fetch */ (function () { if (window.parent === window) { return; } let storage; try { storage = window.localStorage; } catch (e) { storage = window.sessionStorage; } const origin = new URL(document.referrer).origin; const handlers = {}; function addMethod(method, fn) { handlers[method] = function ({uid, options}) { fn(options) .then(function (data) { window.parent.postMessage({uid, data}, origin); }) .catch(function (error) { window.parent.postMessage({uid, error: error.message}, origin); }); }; } function isTokenExpired(token) { try { const [header, claims, signature] = token.split('.'); // eslint-disable-line no-unused-vars const parsedClaims = JSON.parse(atob(claims.replace('+', '-').replace('/', '_'))); const expiry = parsedClaims.exp * 1000; const now = Date.now(); const nearFuture = now + (30 * 1000); if (expiry > nearFuture) { return true; } return false; } catch (e) { return true; } } function getStoredToken(audience) { const tokenKey = 'members:token:aud:' + audience; const storedToken = storage.getItem(tokenKey); if (isTokenExpired(storedToken)) { storage.removeItem(tokenKey); return null; } return storedToken; } function getStoredTokenKeys() { try { return JSON.parse(storage.getItem('members:tokens') || '[]'); } catch (e) { storage.removeItem('members:tokens'); return []; } } function addStoredToken(audience, token) { const storedTokenKeys = getStoredTokenKeys(); const tokenKey = 'members:token:aud:' + audience; storage.setItem(tokenKey, token); if (!storedTokenKeys.includes(tokenKey)) { storage.setItem('members:tokens', JSON.stringify(storedTokenKeys.concat(tokenKey))); } } function clearStorage() { storage.removeItem('signedin'); const storedTokenKeys = getStoredTokenKeys(); storedTokenKeys.forEach(function (key) { storage.removeItem(key); }); storage.removeItem('members:tokens'); } // @TODO this needs to be configurable const membersApi = location.pathname.replace(/\/members\/gateway\/?$/, '/ghost/api/v2/members'); function getToken({audience}) { const storedToken = getStoredToken(audience); if (storedToken) { return Promise.resolve(storedToken); } return fetch(`${membersApi}/token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin, audience: audience || origin }) }).then((res) => { if (!res.ok) { if (res.status === 401) { storage.removeItem('signedin'); } return null; } storage.setItem('signedin', true); return res.text(); }).then(function (token) { if (token) { addStoredToken(audience, token); } return token; }); } addMethod('init', function init() { if (storage.getItem('signedin')) { window.parent.postMessage({event: 'signedin'}, origin); } else { window.parent.postMessage({event: 'signedout'}, origin); } getToken({audience: origin}); return Promise.resolve(); }); addMethod('getToken', getToken); addMethod('createSubscription', function createSubscription({adapter, plan, stripeToken}) { return fetch(`${membersApi}/subscription`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin, adapter, plan, stripeToken }) }).then((res) => { if (res.ok) { storage.setItem('signedin', true); } return res.ok; }); }); addMethod('signin', function signin({email, password}) { return fetch(`${membersApi}/signin`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin, email, password }) }).then((res) => { if (res.ok) { storage.setItem('signedin', true); } return res.ok; }); }); addMethod('signup', function signin({name, email, password}) { return fetch(`${membersApi}/signup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin, name, email, password }) }).then((res) => { if (res.ok) { storage.setItem('signedin', true); } return res.ok; }); }); addMethod('signout', function signout(/*options*/) { return fetch(`${membersApi}/signout`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin }) }).then((res) => { if (res.ok) { clearStorage(); } return res.ok; }); }); addMethod('requestPasswordReset', function requestPasswordReset({email}) { return fetch(`${membersApi}/request-password-reset`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin, email }) }).then((res) => { return res.ok; }); }); addMethod('resetPassword', function resetPassword({token, password}) { return fetch(`${membersApi}/reset-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin, token, password }) }).then((res) => { if (res.ok) { storage.setItem('signedin', true); } return res.ok; }); }); addMethod('getConfig', function getConfig() { return fetch(`${membersApi}/config`, { method: 'GET' }).then((res) => { return res.json(); }); }); window.addEventListener('storage', function (event) { if (event.storageArea !== storage) { return; } const newValue = event.newValue; const oldValue = event.oldValue; if (event.key === 'signedin') { if (newValue && !oldValue) { return window.parent.postMessage({event: 'signedin'}, origin); } if (!newValue && oldValue) { return window.parent.postMessage({event: 'signedout'}, origin); } } }); window.addEventListener('message', function (event) { if (event.origin !== origin) { return; } if (!event.data || !event.data.uid) { return; } if (!handlers[event.data.method]) { return window.parent.postMessage({ uid: event.data.uid, error: 'Unknown method' }, origin); } handlers[event.data.method](event.data); }); })();