Open sickpuppysoftware opened 3 years ago
@sickpuppysoftware To clarify, this is what you are referring to? https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/
Unfortunately, this isn't something we have tested, but we'll add it to our backlog to investigate. We also welcome PRs if you have ideas for specific changes that can be made.
That is what I'm referring to.
@jasonnutter Is there a chance to support that in future ? Our team have exactly the same problem, we use msal for authentication in chrome browser extension which now seems to be impossible due to window
not exist inside of service worker. :(
@jasonnutter +1, it's a blocker for us. Will the problem be solved? Could we know ETA?
Did anyone find a workaround for this issue?
Did anyone find a workaround for this issue?
Kind of yes although I didn't finish it, as a workaround you can use jsdom
and assign it to the global.window, additionally you might need to add polyfills for things like Crypto API(https://developer.mozilla.org/en-US/docs/Web/API/Crypto). Keep in mind that this type of workaround will bloat your background(service worker) script bundle significantly.
hi @bartlomiejzuber , did you finish it? Is your solution working well? I wonder if there are not issues with the localStorage when it's kind of polyfilled from jsdom
Does it actually store the data and load it back when you reopen the browser or when the service worker 'resets'? Or are you using your own caching?
hi @bartlomiejzuber , did you finish it? Is your solution working well? I wonder if there are not issues with the localStorage when it's kind of polyfilled from
jsdom
Does it actually store the data and load it back when you reopen the browser or when the service worker 'resets'? Or are you using your own caching?
Hi, yeah it worked. Not everything can be pollyfilled by jsdom though. When it comes to local storage you need to implement it using chrome's storage API.
Thanks, I have tried this, implementing the localStorage using chrome storage API but I think msal cannot access the value of localStorage.getItem()
as it is async. And real localStorage is not async.
// a workaround for msal not being able to detect browser environment
const JSDOM = new jsdomModule.JSDOM;
globalThis.window = JSDOM.window;
delete globalThis.window.localStorage;
globalThis.document = globalThis.window.document;
globalThis.window.crypto = globalThis.crypto;
// a workaround for msal not supporting chrome.storage API as a cache option
globalThis.localStorage = {
setItem: (keyName, keyValue) => {
console.log(`setting chrome.storage keyName ${keyName} with keyValue ${keyValue}`);
return chrome.storage.local.set({ [`localStorage-${keyName}`]: keyValue });
},
getItem: (keyName) => {
console.log(`getting chrome.storage keyName ${keyName}`);
return chrome.storage.local.get([`localStorage-${keyName}`]);
},
removeItem: (keyName) => {
console.log(`removing chrome.storage keyName ${keyName}`);
return chrome.storage.local.remove([`localStorage-${keyName}`]);
},
clear: () => {
console.log(`clearing chrome.storage`);
return chrome.storage.local.clear();
}
}
globalThis.window.localStorage = {};
globalThis.window.localStorage = globalThis.localStorage;
I think it would require changes in msal code itself to be able to await async values.
Did you do it in a different way?
relation with https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5023
@Alino Hi again, I don't have access to that code anymore but briefly I can explain that I've tried two approaches:
Converting async code to sync by spawning another service worker via navigator.serviceWorker.register
, then doing a sync XHR, intercepting that call inside spawned service worker and putting data there. That didn't work well as finds out that there's a long waiting bug on chrome https://bugs.chromium.org/p/chromium/issues/detail?id=602051 that prevents it from work.
Instead what worked, I've went with simpler approach which was a simple object that was synchronous and that was synced with chrome storage on each update using https://developer.chrome.com/docs/extensions/reference/storage/#event-onChanged. e.g.
let storage = {};
chrome.storage.onChanged.addListener(
(updatedStorage) => storage = updatedStorage
);
globalThis.localStorage = {
getItem: (keyName) => {
console.log(`getting chrome.storage keyName ${keyName}`);
return storage[`localStorage-${keyName}`];
},
}
globalThis.window.localStorage = {};
globalThis.window.localStorage = globalThis.localStorage;
@bartlomiejzuber Hello again, thank you very much for your input. This idea also came across my mind for a while but I kind of ditched it as a dirty hack. But when rethinking it, it is probably the most simple workaround and there isn't anything better without having to refactor the whole MSAL library to use async cache...
I have actually created a sample repository for this. https://github.com/Alino/MSAL_ChromiumExtensionSampleManifestV3
I am polyfilling the window.crypto object with msrCrypto which is also a lib from Microsoft. Did you use also this library or anything else? I have an issue with this one. When trying to signIn, it throws an error with Detail: algorithm
If you are aware of some polyfill that works that would be great, thanks :)
I have tried few others but none of them worked and this one from MS looks best, yet it still does not work.
EDIT: I have asked the question about incompatibility with msrCrypto here -> https://github.com/AzureAD/microsoft-authentication-library-for-js/discussions/5079
I have created a sample repo with OIDC-client-ts lib instead of MSAL https://github.com/Alino/OIDC-client-ts-chromium-sample
It actually works, I can sign in and sign out...
Hi, is there any known workaround for this issue? Considering chrome has stopped accepting manifest v2 extensions on the chrome store, this will be creating a problem for my team to publish our extension.
This is a problem when using Blazor Webassembly with Azure AD B2C for Edge and Chrome extensions. Is there any plan to address this with .NET 8 release?
The MSAL.js team is tracking on our backlog adding manifest v3 support, but I don't have a timeline to share. We are also monitoring Chrome's review of their support timeline for manifest v2.
@chaitanyakale I don't have insight into the .NET 8 release, but Blazor Webassembly wraps MSAL.js for Azure AD B2C so it will not have support until MSAL.js does.
Is there any way to publish a new Edge/Chrome extension with manifest v2 that uses MSAL.js?
Any update on this ?
Okay, for anyone interested, we've managed to make it (sort of) work with Manifest V3 service worker via use of the offscreen API.
The idea is that you do the login and MSAL initialization through popup and if you need to call the API with access token from MSAL, you send the message to offscreen script running in the background, which can access cached token and refresh it if necessary. So, assuming you have login code already working on the popup side of things, here's how to get token in background service worker via offscreen script.
Add new permission to your manifest:
"permissions": [
...,
"offscreen"
]
Create a new HTML page that is just loading the script. In our case we are using TypeScript, so we loaded the .ts file as a module:
<!DOCTYPE html>
<script src="offscreen.ts" type="module"></script>
Create a new script file that will contain one function, responsible for getting the access token from MSAL auth provider:
const getAccessToken = async () => {
const authProvider = [get your auth provider however you do in your project];
const authResponse = await authProvider?.acquireToken();
return authResponse?.accessToken;
};
Since offscreen can access only chrome.runtime
API, we can subscribe to message from background script, and on said message we will fetch the token and pass it back to background service worker. So, below getAccessToken
in your offscreen script add following code:
chrome.runtime.onMessage.addListener((message, _, sendResponse) => {
const getToken = async () => {
const token = await getAccessToken();
sendResponse(token);
};
if (message === 'getAccessTokenViaOffscreen') {
getToken();
// This indicates that the response will be sent asynchronously
return true;
}
});
In your background service worker, you'll have to spin the offscreen script first, then trigger the token acquisition via chrome.runtime.sendMessage
method and at the end, you might want to close the offscreen:
const OFFSCREEN_DOCUMENT_PATH = '/offscreen.html';
// A global promise to avoid concurrency issues let creatingOffscreenDocument;
// Chrome only allows for a single offscreenDocument. This is a helper function // that returns a boolean indicating if a document is already active. async function hasDocument() { // Check all windows controlled by the service worker to see if one // of them is the offscreen document with the given path const matchedClients = await clients.matchAll(); return matchedClients.some( (c) => c.url === chrome.runtime.getURL(OFFSCREEN_DOCUMENT_PATH) ); }
async function setupOffscreenDocument(path) { // If we do not have a document, we are already setup and can skip if (!(await hasDocument())) { // create offscreen document if (creating) { await creating; } else { creating = chrome.offscreen.createDocument({ url: path, reasons: [ chrome.offscreen.Reason.DOM_SCRAPING ], justification: 'authentication' }); await creating; creating = null; } } }
async function closeOffscreenDocument() { if (!(await hasDocument())) { return; } await chrome.offscreen.closeDocument(); }
async function getAccessToken() { await setupOffscreenDocument(OFFSCREEN_DOCUMENT_PATH); const token = await chrome.runtime.sendMessage('getAccessTokenViaOffscreen'); await closeOffscreenDocument();
return token; }
This solution was based on how Firebase handles this in Manifest V3 extensions, outlined [here](https://firebase.google.com/docs/auth/web/chrome-extension), but since we were using MSAL extensively already, we've just adjusted this method to work with MSAL.
Hopefully that will help some of you!
Core Library
MSAL.js v2 (@azure/msal-browser)
Wrapper Library
Not Applicable
Description
msal-browser doesn't work as it should when called from a manifest v3 service worker. I've adapted the extension sample in an attempt to get it working under manifest v3. However I have the following issues depending on where I try to use the library. Background service worker - The library's browser detection fails as there is no window object in a service worker. Content script - No access to chrome.identity should library calls fail. I'm also loathed to inject the whole library into every page loaded.
Is there ay way to get the library working with manifest v3?
Source
External (Customer)