feat: oauth for chrome extension (#4870)
Previously we had to create a separate API key to give access to chrome extension so we can make calls to the DB. This PR includes logic to initiate a oauth flow with PKCE method which redirects to the `Authorise` screen to give access to server tokens. Implemented in this PR- 1. make `redirectUrl` a non-nullable parameter 2. Add `NODE_ENV` to environment variable service 3. new env variable `CHROME_EXTENSION_REDIRECT_URL` on server side 4. strict checks for redirectUrl 5. try catch blocks on utils db query methods 6. refactor Apollo Client to handle `unauthorized` condition 7. input field to enter server url (for self-hosting) 8. state to show user if its already connected 9. show error if oauth flow is cancelled by user Follow up PR - Renew token logic --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -52,12 +52,13 @@ export const createDefaultButton = (
|
||||
Object.assign(div.style, divStyles);
|
||||
});
|
||||
|
||||
// Handle the click event.
|
||||
div.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
const store = await chrome.storage.local.get();
|
||||
|
||||
// If an api key is not set, the options page opens up to allow the user to configure an api key.
|
||||
if (!store.apiKey) {
|
||||
if (!store.accessToken) {
|
||||
chrome.runtime.sendMessage({ action: 'openOptionsPage' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { insertButtonForCompany } from '~/contentScript/extractCompanyProfile';
|
||||
import { insertButtonForPerson } from '~/contentScript/extractPersonProfile';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
// Inject buttons into the DOM when SPA is reloaded on the resource url.
|
||||
// e.g. reload the page when on https://www.linkedin.com/in/mabdullahabaid/
|
||||
@ -20,20 +21,26 @@ chrome.runtime.onMessage.addListener(async (message, _, sendResponse) => {
|
||||
}
|
||||
|
||||
if (message.action === 'TOGGLE') {
|
||||
toggle();
|
||||
await toggle();
|
||||
}
|
||||
|
||||
if (message.action === 'AUTHENTICATED') {
|
||||
await authenticated();
|
||||
}
|
||||
|
||||
sendResponse('Executing!');
|
||||
});
|
||||
|
||||
const IFRAME_WIDTH = '400px';
|
||||
|
||||
const createIframe = () => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.background = 'lightgrey';
|
||||
iframe.style.height = '100vh';
|
||||
iframe.style.width = '400px';
|
||||
iframe.style.width = IFRAME_WIDTH;
|
||||
iframe.style.position = 'fixed';
|
||||
iframe.style.top = '0px';
|
||||
iframe.style.right = '-400px';
|
||||
iframe.style.right = `-${IFRAME_WIDTH}`;
|
||||
iframe.style.zIndex = '9000000000000000000';
|
||||
iframe.style.transition = 'ease-in-out 0.3s';
|
||||
return iframe;
|
||||
@ -41,33 +48,57 @@ const createIframe = () => {
|
||||
|
||||
const handleContentIframeLoadComplete = () => {
|
||||
//If the pop-out window is already open then we replace loading iframe with our content iframe
|
||||
if (loadingIframe.style.right === '0px') contentIframe.style.right = '0px';
|
||||
loadingIframe.style.display = 'none';
|
||||
if (optionsIframe.style.right === '0px') contentIframe.style.right = '0px';
|
||||
optionsIframe.style.display = 'none';
|
||||
contentIframe.style.display = 'block';
|
||||
};
|
||||
|
||||
//Creating one iframe where we are loading our front end in the background
|
||||
const contentIframe = createIframe();
|
||||
contentIframe.style.display = 'none';
|
||||
contentIframe.src = `${import.meta.env.VITE_FRONT_BASE_URL}`;
|
||||
contentIframe.onload = handleContentIframeLoadComplete;
|
||||
|
||||
//Creating this iframe to show as a loading state until the above iframe loads completely
|
||||
const loadingIframe = createIframe();
|
||||
loadingIframe.src = chrome.runtime.getURL('loading.html');
|
||||
chrome.storage.local.get().then((store) => {
|
||||
if (isDefined(store.loginToken)) {
|
||||
contentIframe.src = `${import.meta.env.VITE_FRONT_BASE_URL}`;
|
||||
contentIframe.onload = handleContentIframeLoadComplete;
|
||||
}
|
||||
});
|
||||
|
||||
const optionsIframe = createIframe();
|
||||
optionsIframe.src = chrome.runtime.getURL('options.html');
|
||||
|
||||
document.body.appendChild(loadingIframe);
|
||||
document.body.appendChild(contentIframe);
|
||||
document.body.appendChild(optionsIframe);
|
||||
|
||||
const toggleIframe = (iframe: HTMLIFrameElement) => {
|
||||
if (iframe.style.right === '-400px' && iframe.style.display !== 'none') {
|
||||
if (
|
||||
iframe.style.right === `-${IFRAME_WIDTH}` &&
|
||||
iframe.style.display !== 'none'
|
||||
) {
|
||||
iframe.style.right = '0px';
|
||||
} else if (iframe.style.right === '0px' && iframe.style.display !== 'none') {
|
||||
iframe.style.right = '-400px';
|
||||
iframe.style.right = `-${IFRAME_WIDTH}`;
|
||||
}
|
||||
};
|
||||
|
||||
const toggle = () => {
|
||||
toggleIframe(loadingIframe);
|
||||
toggleIframe(contentIframe);
|
||||
const toggle = async () => {
|
||||
const store = await chrome.storage.local.get();
|
||||
if (isDefined(store.accessToken)) {
|
||||
toggleIframe(contentIframe);
|
||||
} else {
|
||||
toggleIframe(optionsIframe);
|
||||
}
|
||||
};
|
||||
|
||||
const authenticated = async () => {
|
||||
const store = await chrome.storage.local.get();
|
||||
if (isDefined(store.loginToken)) {
|
||||
contentIframe.src = `${
|
||||
import.meta.env.VITE_FRONT_BASE_URL
|
||||
}/verify?loginToken=${store.loginToken.token}`;
|
||||
contentIframe.onload = handleContentIframeLoadComplete;
|
||||
toggleIframe(contentIframe);
|
||||
} else {
|
||||
toggleIframe(optionsIframe);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user