Este documento mostra como usar a Identity Platform para permitir que os utilizadores iniciem sessão numa extensão do Chrome que usa o Manifest V3.
O Identity Platform oferece vários métodos de autenticação para iniciar sessão em utilizadores a partir de uma extensão do Chrome. Alguns requerem mais esforço de desenvolvimento do que outros.
Para usar os seguintes métodos numa extensão do Chrome do Manifest V3, só precisa de
os importar de firebase/auth/web-extension
:
- Inicie sessão com email e palavra-passe (
createUserWithEmailAndPassword
esignInWithEmailAndPassword
) - Inicie sessão com um link por email (
sendSignInLinkToEmail
,isSignInWithEmailLink
esignInWithEmailLink
) - Inicie sessão anonimamente (
signInAnonymously
) - Inicie sessão com um sistema de autenticação personalizado (
signInWithCustomToken
) - Processar o início de sessão do fornecedor de forma independente e, em seguida, usar
signInWithCredential
Os seguintes métodos de início de sessão também são suportados, mas requerem algum trabalho adicional:
- Inicie sessão com uma janela de pop-up (
signInWithPopup
,linkWithPopup
ereauthenticateWithPopup
) - Inicie sessão através do redirecionamento para a página de início de sessão (
signInWithRedirect
,linkWithRedirect
ereauthenticateWithRedirect
) - Inicie sessão com o número de telefone com o reCAPTCHA
- Autenticação multifator por SMS com reCAPTCHA
- Proteção do reCAPTCHA Enterprise
Para usar estes métodos numa extensão do Chrome com o Manifest V3, tem de usar os documentos fora do ecrã.
Use o ponto de entrada firebase/auth/web-extension
A importação a partir de firebase/auth/web-extension
faz com que o início de sessão dos utilizadores a partir de uma extensão do Chrome seja semelhante ao de uma app Web.
firebase/auth/web-extension só é suportado nas versões v10.8.0 e superiores do SDK para a Web.
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth/web-extension'; const auth = getAuth(); signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { // Signed in const user = userCredential.user; // ... }) .catch((error) => { const errorCode = error.code; const errorMessage = error.message; });
Use documentos fora do ecrã
Alguns métodos de autenticação, como signInWithPopup
, linkWithPopup
e reauthenticateWithPopup
, não são diretamente compatíveis com extensões do Chrome, porque requerem que o código seja carregado a partir de fora do pacote de extensão.
No Manifest V3, isto não é permitido e é bloqueado pela plataforma de extensões. Para contornar esta situação, pode carregar esse código num iFrame através de um documento fora do ecrã.
No documento fora do ecrã, implemente o fluxo de autenticação normal e encaminhe o resultado do documento fora do ecrã de volta para a extensão.
Este guia usa o signInWithPopup
como exemplo, mas o mesmo conceito aplica-se a outros métodos de autenticação.
Antes de começar
Esta técnica requer que configure uma página Web disponível na Web que vai carregar num iFrame. Qualquer anfitrião funciona para este fim, incluindo o Firebase Hosting. Crie um Website com o seguinte conteúdo:
<!DOCTYPE html> <html> <head> <title>signInWithPopup</title> <script src="signInWithPopup.js"></script> </head> <body><h1>signInWithPopup</h1></body> </html>
Início de sessão federado
Se estiver a usar o início de sessão federado, como o início de sessão com o Google, a Apple, o SAML ou o OIDC, tem de adicionar o ID da extensão do Chrome à lista de domínios autorizados:
Aceda à página Definições do Identity Platform na Google Cloud consola.
Selecione o separador Segurança.
Em Domínios autorizados, clique em Adicionar domínio.
Introduza o URI da extensão. Deve ter um aspeto semelhante a
chrome-extension://CHROME_EXTENSION_ID
.Clique em Adicionar.
No ficheiro de manifesto da extensão do Chrome, certifique-se de que adiciona os seguintes URLs à lista de permissões content_security_policy
:
https://apis.google.com
https://www.gstatic.com
https://www.googleapis.com
https://securetoken.googleapis.com
Implemente a autenticação
No seu documento HTML, signInWithPopup.js é o código JavaScript que processa a autenticação. Existem duas formas diferentes de implementar um método que é diretamente suportado na extensão:
- Use
firebase/auth/web-extension
no código da extensão, como scripts em segundo plano, trabalhadores de serviço ou scripts de pop-up. Usefirebase/auth
apenas no iFrame fora do ecrã, porque esse iFrame é executado num contexto de página Web padrão. - Encapsule a lógica de autenticação num ouvinte
postMessage
para encaminhar o pedido e a resposta de autenticação.
import { signInWithPopup, GoogleAuthProvider, getAuth } from'firebase/auth'; import { initializeApp } from 'firebase/app'; import firebaseConfig from './firebaseConfig.js' const app = initializeApp(firebaseConfig); const auth = getAuth(); // This code runs inside of an iframe in the extension's offscreen document. // This gives you a reference to the parent frame, i.e. the offscreen document. // You will need this to assign the targetOrigin for postMessage. const PARENT_FRAME = document.location.ancestorOrigins[0]; // This demo uses the Google auth provider, but any supported provider works. // Make sure that you enable any provider you want to use in the Firebase Console. // https://console.firebase.google.com/project/_/authentication/providers const PROVIDER = new GoogleAuthProvider(); function sendResponse(result) { globalThis.parent.self.postMessage(JSON.stringify(result), PARENT_FRAME); } globalThis.addEventListener('message', function({data}) { if (data.initAuth) { // Opens the Google sign-in page in a popup, inside of an iframe in the // extension's offscreen document. // To centralize logic, all respones are forwarded to the parent frame, // which goes on to forward them to the extension's service worker. signInWithPopup(auth, PROVIDER) .then(sendResponse) .catch(sendResponse) } });
Crie a sua extensão do Chrome
Depois de o seu Website estar ativo, pode usá-lo na extensão do Chrome.
- Adicione a autorização
offscreen
ao ficheiro manifest.json: - Crie o próprio documento fora do ecrã. Este é um ficheiro HTML mínimo no pacote da extensão que carrega a lógica do JavaScript do documento fora do ecrã:
- Inclua
offscreen.js
no pacote de extensão. Funciona como o proxy entre o Website público configurado no passo 1 e a sua extensão. - Configure o documento fora do ecrã a partir do service worker background.js.
{ "name": "signInWithPopup Demo", "manifest_version" 3, "background": { "service_worker": "background.js" }, "permissions": [ "offscreen" ] }
<!DOCTYPE html> <script src="./offscreen.js"></script>
// This URL must point to the public site const _URL = 'https://example.com/signInWithPopupExample'; const iframe = document.createElement('iframe'); iframe.src = _URL; document.documentElement.appendChild(iframe); chrome.runtime.onMessage.addListener(handleChromeMessages); function handleChromeMessages(message, sender, sendResponse) { // Extensions may have an number of other reasons to send messages, so you // should filter out any that are not meant for the offscreen document. if (message.target !== 'offscreen') { return false; } function handleIframeMessage({data}) { try { if (data.startsWith('!_{')) { // Other parts of the Firebase library send messages using postMessage. // You don't care about them in this context, so return early. return; } data = JSON.parse(data); self.removeEventListener('message', handleIframeMessage); sendResponse(data); } catch (e) { console.log(`json parse failed - ${e.message}`); } } globalThis.addEventListener('message', handleIframeMessage, false); // Initialize the authentication flow in the iframed document. You must set the // second argument (targetOrigin) of the message in order for it to be successfully // delivered. iframe.contentWindow.postMessage({"initAuth": true}, new URL(_URL).origin); return true; }
import { getAuth } from 'firebase/auth/web-extension'; 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(); } function getAuth() { return new Promise(async (resolve, reject) => { const auth = await chrome.runtime.sendMessage({ type: 'firebase-auth', target: 'offscreen' }); auth?.name !== 'FirebaseError' ? resolve(auth) : reject(auth); }) } async function firebaseAuth() { await setupOffscreenDocument(OFFSCREEN_DOCUMENT_PATH); const auth = await getAuth() .then((auth) => { console.log('User Authenticated', auth); return auth; }) .catch(err => { if (err.code === 'auth/operation-not-allowed') { console.error('You must enable an OAuth provider in the Firebase' + ' console in order to use signInWithPopup. This sample' + ' uses Google by default.'); } else { console.error(err); return err; } }) .finally(closeOffscreenDocument) return auth; }
Agora, quando chama firebaseAuth()
no service worker, cria o documento fora do ecrã e carrega o site num iFrame. Esse iFrame é processado
em segundo plano e o Firebase passa pelo fluxo de autenticação
padrão. Assim que for resolvida ou rejeitada, o objeto de autenticação é encaminhado do iframe para o service worker através do documento fora do ecrã.