Authentifizierung zu deiner Chrome-Erweiterungsanwendung hinzufügen
Dieser Leitfaden zeigt dir, wie du Logto in deine Chrome-Erweiterung integrierst.
- Die folgende Demonstration wurde auf Chrome v123.0.6312.87 (arm64) getestet. Andere Versionen sollten ebenfalls funktionieren, solange sie die im SDK verwendeten
chrome
APIs unterstützen. - Das Beispielprojekt ist in unserem GitHub-Repository verfügbar.
Voraussetzungen
- Ein Logto Cloud Konto oder ein selbst gehostetes Logto.
- Eine Single-Page-Anwendung (SPA), die in der Logto-Konsole erstellt wurde.
- Ein Chrome-Erweiterungsprojekt.
Installation
- npm
- Yarn
- pnpm
npm i @logto/chrome-extension
yarn add @logto/chrome-extension
pnpm add @logto/chrome-extension
Integration
Der Authentifizierungsablauf
Angenommen, du platzierst einen "Anmelden"-Button im Popup deiner Chrome-Erweiterung, sieht der Authentifizierungsablauf folgendermaßen aus:
Für andere interaktive Seiten in deiner Erweiterung musst du nur den Teilnehmer Erweiterungspopup
durch den Namen der Seite ersetzen. In diesem Tutorial konzentrieren wir uns auf die Popup-Seite.
Bezüglich der umleitungsbasierten Anmeldung
- Dieser Authentifizierungsprozess folgt dem OpenID Connect (OIDC) Protokoll, und Logto erzwingt strenge Sicherheitsmaßnahmen, um die Benutzeranmeldung zu schützen.
- Wenn du mehrere Apps hast, kannst du denselben Identitätsanbieter (Logto) verwenden. Sobald sich der Benutzer bei einer App anmeldet, wird Logto den Anmeldeprozess automatisch abschließen, wenn der Benutzer auf eine andere App zugreift.
Um mehr über die Gründe und Vorteile der umleitungsbasierten Anmeldung zu erfahren, siehe Logto-Anmeldeerfahrung erklärt.
Aktualisiere die manifest.json
Das Logto SDK erfordert die folgenden Berechtigungen in der manifest.json
:
{
"permissions": ["identity", "storage"],
"host_permissions": ["https://*.logto.app/*"]
}
permissions.identity
: Erforderlich für die Chrome Identity API, die zum Anmelden und Abmelden verwendet wird.permissions.storage
: Erforderlich zum Speichern der Benutzersitzung.host_permissions
: Erforderlich, damit das Logto SDK mit den Logto APIs kommunizieren kann.
Wenn du eine benutzerdefinierte Domain auf Logto Cloud verwendest, musst du die host_permissions
aktualisieren, um deine Domain anzupassen.
Richte ein Hintergrundskript (Service-Worker) ein
In deinem Hintergrundskript der Chrome-Erweiterung initialisiere das Logto SDK:
import LogtoClient from '@logto/chrome-extension';
export const logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>'
appId: '<your-logto-app-id>',
});
Ersetze <your-logto-endpoint>
und <your-logto-app-id>
durch die tatsächlichen Werte. Du findest diese Werte auf der Anwendungsseite, die du gerade im Logto Console erstellt hast.
Wenn du kein Hintergrundskript hast, kannst du der offiziellen Anleitung folgen, um eines zu erstellen.
Warum benötigen wir ein Hintergrundskript?
Normale Erweiterungsseiten wie das Popup oder die Optionsseite können nicht im Hintergrund ausgeführt werden und könnten während des Authentifizierungsprozesses geschlossen werden. Ein Hintergrundskript stellt sicher, dass der Authentifizierungsprozess ordnungsgemäß abgewickelt werden kann.
Dann müssen wir auf Nachrichten von anderen Erweiterungsseiten hören und den Authentifizierungsprozess abwickeln:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Im folgenden Code, da wir `true` für jede Aktion zurückgeben, müssen wir `sendResponse` aufrufen,
// um den Absender zu benachrichtigen. Du kannst hier auch Fehler behandeln oder andere Wege nutzen, um den Absender zu benachrichtigen.
if (message.action === 'signIn') {
const redirectUri = chrome.identity.getRedirectURL('/callback');
logtoClient.signIn(redirectUri).finally(sendResponse);
return true;
}
if (message.action === 'signOut') {
const redirectUri = chrome.identity.getRedirectURL();
logtoClient.signOut(redirectUri).finally(sendResponse);
return true;
}
return false;
});
Du wirst bemerken, dass im obigen Code zwei Redirect-URIs verwendet werden. Beide werden durch chrome.identity.getRedirectURL
erstellt, eine eingebaute Chrome API, um eine Redirect-URL für Authentifizierungsabläufe zu generieren. Die beiden URIs werden sein:
https://<extension-id>.chromiumapp.org/callback
für die Anmeldung.https://<extension-id>.chromiumapp.org/
für die Abmeldung.
Beachte, dass diese URIs nicht zugänglich sind und nur von Chrome verwendet werden, um bestimmte Aktionen für den Authentifizierungsprozess auszulösen.
Aktualisiere die Logto-Anwendungseinstellungen
Jetzt müssen wir die Logto-Anwendungseinstellungen aktualisieren, um die gerade erstellten Redirect-URIs zuzulassen.
- Gehe zur Anwendungsseite im Logto Console.
- Füge im Abschnitt "Redirect URIs" die URI hinzu:
https://<extension-id>.chromiumapp.org/callback
. - Füge im Abschnitt "Post sign-out redirect URIs" die URI hinzu:
https://<extension-id>.chromiumapp.org/
. - Füge im Abschnitt "CORS erlaubte Ursprünge" die URI hinzu:
chrome-extension://<extension-id>
. Das SDK in der Chrome-Erweiterung wird diesen Ursprung verwenden, um mit den Logto APIs zu kommunizieren. - Klicke auf Änderungen speichern.
Denke daran, <extension-id>
durch deine tatsächliche Erweiterungs-ID zu ersetzen. Du findest die Erweiterungs-ID auf der Seite chrome://extensions
.
Füge Anmelde- und Abmeldebuttons zum Popup hinzu
Wir sind fast fertig! Lass uns die Anmelde- und Abmeldebuttons und andere notwendige Logik zur Popup-Seite hinzufügen.
In der Datei popup.html
:
<button id="sign-in">Anmelden</button> <button id="sign-out">Abmelden</button>
In der Datei popup.js
(angenommen, popup.js
ist in popup.html
enthalten):
document.getElementById('sign-in').addEventListener('click', async () => {
await chrome.runtime.sendMessage({ action: 'signIn' });
// Anmeldung abgeschlossen (oder fehlgeschlagen), du kannst hier die Benutzeroberfläche aktualisieren.
});
document.getElementById('sign-out').addEventListener('click', async () => {
await chrome.runtime.sendMessage({ action: 'signOut' });
// Abmeldung abgeschlossen (oder fehlgeschlagen), du kannst hier die Benutzeroberfläche aktualisieren.
});
Checkpoint: Teste den Authentifizierungsablauf
Jetzt kannst du den Authentifizierungsablauf in deiner Chrome-Erweiterung testen:
- Öffne das Erweiterungspopup.
- Klicke auf den "Anmelden"-Button.
- Du wirst zur Logto-Anmeldeseite weitergeleitet.
- Melde dich mit deinem Logto-Konto an.
- Du wirst zurück zu Chrome weitergeleitet.
Überprüfe den Authentifizierungsstatus
Da Chrome einheitliche Speicher-APIs bereitstellt, können alle anderen Logto SDK-Methoden, abgesehen vom Anmelde- und Abmeldeablauf, direkt auf der Popup-Seite verwendet werden.
In deinem popup.js
kannst du die LogtoClient
-Instanz aus dem Hintergrundskript wiederverwenden oder eine neue mit derselben Konfiguration erstellen:
import LogtoClient from '@logto/chrome-extension';
const logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>'
appId: '<your-logto-app-id>',
});
// Oder die logtoClient-Instanz aus dem Hintergrundskript wiederverwenden
import { logtoClient } from './service-worker.js';
Dann kannst du eine Funktion erstellen, um den Authentifizierungsstatus und das Benutzerprofil zu laden:
const loadAuthenticationState = async () => {
const isAuthenticated = await logtoClient.isAuthenticated();
// Aktualisiere die Benutzeroberfläche basierend auf dem Authentifizierungsstatus
if (isAuthenticated) {
const user = await logtoClient.getIdTokenClaims(); // { sub: '...', email: '...', ... }
// Aktualisiere die Benutzeroberfläche mit dem Benutzerprofil
}
};
Du kannst die Funktion loadAuthenticationState
auch mit der Anmelde- und Abmeldelogik kombinieren:
document.getElementById('sign-in').addEventListener('click', async () => {
await chrome.runtime.sendMessage({ action: 'signIn' });
await loadAuthenticationState();
});
document.getElementById('sign-out').addEventListener('click', async () => {
await chrome.runtime.sendMessage({ action: 'signOut' });
await loadAuthenticationState();
});
Hier ist ein Beispiel der Popup-Seite mit dem Authentifizierungsstatus:
Weitere Überlegungen
- Service-Worker-Bundling: Wenn du einen Bundler wie Webpack oder Rollup verwendest, musst du das Ziel explizit auf
browser
oder ähnlich setzen, um unnötiges Bundling von Node.js-Modulen zu vermeiden. - Modulauflösung: Das Logto Chrome-Erweiterungs-SDK ist ein ESM-only-Modul.
Siehe unser Beispielprojekt für ein vollständiges Beispiel mit TypeScript, Rollup und anderen Konfigurationen.
Benutzerinformationen abrufen
Benutzerinformationen anzeigen
Um die Informationen des Benutzers anzuzeigen, kannst du die Methode logtoClient.getIdTokenClaims()
verwenden. Zum Beispiel auf deiner Startseite:
const userInfo = await logtoClient.getIdTokenClaims();
// Anzeige-Tabelle für ID-Token-Ansprüche generieren
const table = document.createElement('table');
const thead = document.createElement('thead');
const tr = document.createElement('tr');
const thName = document.createElement('th');
const thValue = document.createElement('th');
thName.innerHTML = 'Name';
thValue.innerHTML = 'Wert';
tr.append(thName, thValue);
thead.append(tr);
table.append(thead);
const tbody = document.createElement('tbody');
for (const [key, value] of Object.entries(userInfo)) {
const tr = document.createElement('tr');
const tdName = document.createElement('td');
const tdValue = document.createElement('td');
tdName.innerHTML = key;
tdValue.innerHTML = typeof value === 'string' ? value : JSON.stringify(value);
tr.append(tdName, tdValue);
tbody.append(tr);
}
table.append(tbody);
Zusätzliche Ansprüche anfordern
Möglicherweise fehlen einige Benutzerinformationen im zurückgegebenen Objekt von getIdTokenClaims()
. Dies liegt daran, dass OAuth 2.0 und OpenID Connect (OIDC) so konzipiert sind, dass sie dem Prinzip der minimalen Rechte (PoLP) folgen, und Logto auf diesen Standards basiert.
Standardmäßig werden begrenzte Ansprüche zurückgegeben. Wenn du mehr Informationen benötigst, kannst du zusätzliche Berechtigungen anfordern, um auf mehr Ansprüche zuzugreifen.
Ein "Anspruch (Claim)" ist eine Behauptung über ein Subjekt; eine "Berechtigung (Scope)" ist eine Gruppe von Ansprüchen. Im aktuellen Fall ist ein Anspruch ein Informationsstück über den Benutzer.
Hier ist ein nicht-normatives Beispiel für die Beziehung zwischen Berechtigung und Anspruch:
Der "sub"-Anspruch bedeutet "Subjekt", was der eindeutige Identifikator des Benutzers ist (d. h. Benutzer-ID).
Das Logto SDK wird immer drei Berechtigungen anfordern: openid
, profile
und offline_access
.
Um zusätzliche Berechtigungen anzufordern, kannst du die Logto-Konfigurationen anpassen:
import LogtoClient, { UserScope } from '@logto/browser';
const logtoClient = new LogtoClient({
appId: '<your-application-id>',
endpoint: '<your-logto-endpoint>',
scopes: [UserScope.Email, UserScope.Phone],
});
Dann kannst du auf die zusätzlichen Ansprüche im Rückgabewert von logtoClient.getIdTokenClaims()
zugreifen:
const claims = await getIdTokenClaims();
// Jetzt kannst du auf zusätzliche Ansprüche wie `claims.email`, `claims.phone` usw. zugreifen.
Ansprüche, die Netzwerk-Anfragen benötigen
Um das ID-Token nicht aufzublähen, erfordern einige Ansprüche Netzwerk-Anfragen, um abgerufen zu werden. Zum Beispiel ist der custom_data
Anspruch nicht im Benutzerobjekt enthalten, selbst wenn er in den Berechtigungen angefordert wird. Um auf diese Ansprüche zuzugreifen, kannst du die logtoClient.fetchUserInfo()
Methode verwenden:
const userInfo = await logtoClient.fetchUserInfo();
// Jetzt kannst du auf den Anspruch `userInfo.custom_data` zugreifen.
Berechtigungen und Ansprüche
Logto verwendet OIDC Berechtigungen und Ansprüche Konventionen, um die Berechtigungen und Ansprüche für das Abrufen von Benutzerinformationen aus dem ID-Token und dem OIDC userinfo endpoint zu definieren. Sowohl "Berechtigung (Scope)" als auch "Anspruch (Claim)" sind Begriffe aus den OAuth 2.0 und OpenID Connect (OIDC) Spezifikationen.
Hier ist die Liste der unterstützten Berechtigungen (Scopes) und der entsprechenden Ansprüche (Claims):
openid
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
sub | string | Der eindeutige Identifikator des Benutzers | Nein |
profile
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
name | string | Der vollständige Name des Benutzers | Nein |
username | string | Der Benutzername des Benutzers | Nein |
picture | string | URL des Profilbildes des Endbenutzers. Diese URL MUSS auf eine Bilddatei (zum Beispiel eine PNG-, JPEG- oder GIF-Bilddatei) verweisen und nicht auf eine Webseite, die ein Bild enthält. Beachte, dass diese URL speziell auf ein Profilfoto des Endbenutzers verweisen SOLLTE, das geeignet ist, den Endbenutzer zu beschreiben, und nicht auf ein beliebiges Foto, das vom Endbenutzer aufgenommen wurde. | Nein |
created_at | number | Zeitpunkt, zu dem der Endbenutzer erstellt wurde. Die Zeit wird als Anzahl der Millisekunden seit der Unix-Epoche (1970-01-01T00:00:00Z) dargestellt. | Nein |
updated_at | number | Zeitpunkt, zu dem die Informationen des Endbenutzers zuletzt aktualisiert wurden. Die Zeit wird als Anzahl der Millisekunden seit der Unix-Epoche (1970-01-01T00:00:00Z) dargestellt. | Nein |
Andere Standardansprüche wie family_name
, given_name
, middle_name
, nickname
, preferred_username
, profile
, website
, gender
, birthdate
, zoneinfo
und locale
werden ebenfalls im profile
-Scope enthalten sein, ohne dass der userinfo-Endpunkt angefordert werden muss. Ein Unterschied im Vergleich zu den oben genannten Ansprüchen besteht darin, dass diese Ansprüche nur zurückgegeben werden, wenn ihre Werte nicht leer sind, während die oben genannten Ansprüche null
zurückgeben, wenn die Werte leer sind.
Im Gegensatz zu den Standardansprüchen verwenden die Ansprüche created_at
und updated_at
Millisekunden anstelle von Sekunden.
email
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
string | Die E-Mail-Adresse des Benutzers | Nein | |
email_verified | boolean | Ob die E-Mail-Adresse verifiziert wurde | Nein |
phone
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
phone_number | string | Die Telefonnummer des Benutzers | Nein |
phone_number_verified | boolean | Ob die Telefonnummer verifiziert wurde | Nein |
address
Bitte siehe OpenID Connect Core 1.0 für die Details des Adressanspruchs.
custom_data
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
custom_data | object | Die benutzerdefinierten Daten des Benutzers | Ja |
identities
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
identities | object | Die verknüpften Identitäten des Benutzers | Ja |
sso_identities | array | Die verknüpften SSO-Identitäten des Benutzers | Ja |
urn:logto:scope:organizations
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
organizations | string[] | Die Organisations-IDs, denen der Benutzer angehört | Nein |
organization_data | object[] | Die Organisationsdaten, denen der Benutzer angehört | Ja |
urn:logto:scope:organization_roles
Anspruchsname | Typ | Beschreibung | Benötigt userinfo? |
---|---|---|---|
organization_roles | string[] | Die Organisationsrollen, denen der Benutzer angehört, im Format <organization_id>:<role_name> | Nein |
In Anbetracht der Leistung und der Datengröße bedeutet "Benötigt userinfo?" "Ja", dass der Anspruch nicht im ID-Token angezeigt wird, sondern in der Antwort des userinfo-Endpunkts zurückgegeben wird.
API-Ressourcen
Wir empfehlen, zuerst 🔐 Rollenbasierte Zugangskontrolle (RBAC) zu lesen, um die grundlegenden Konzepte von Logto RBAC zu verstehen und wie man API-Ressourcen richtig einrichtet.
Logto-Client konfigurieren
Sobald du die API-Ressourcen eingerichtet hast, kannst du sie bei der Konfiguration von Logto in deiner App hinzufügen:
import LogtoClient from '@logto/browser';
const logtoClient = new LogtoClient({
// ...other configs
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API-Ressourcen hinzufügen
});
Jede API-Ressource hat ihre eigenen Berechtigungen (Berechtigungen).
Zum Beispiel hat die Ressource https://shopping.your-app.com/api
die Berechtigungen shopping:read
und shopping:write
, und die Ressource https://store.your-app.com/api
hat die Berechtigungen store:read
und store:write
.
Um diese Berechtigungen anzufordern, kannst du sie bei der Konfiguration von Logto in deiner App hinzufügen:
import LogtoClient from '@logto/chrome-extension';
const logtoClient = new LogtoClient({
// ...other configs
scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'],
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API-Ressourcen hinzufügen
});
Du wirst bemerken, dass Berechtigungen separat von API-Ressourcen definiert sind. Dies liegt daran, dass Resource Indicators for OAuth 2.0 spezifiziert, dass die endgültigen Berechtigungen für die Anfrage das kartesische Produkt aller Berechtigungen bei allen Zielservices sein werden.
Somit können im obigen Fall die Berechtigungen aus der Definition in Logto vereinfacht werden, beide API-Ressourcen können read
und write
Berechtigungen ohne Präfix haben. Dann, in der Logto-Konfiguration:
import LogtoClient, { UserScope } from '@logto/chrome-extension';
const logtoClient = new LogtoClient({
// ...other configs
scopes: ['read', 'write'],
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'],
});
Für jede API-Ressource wird sowohl read
als auch write
Berechtigungen angefordert.
Es ist in Ordnung, Berechtigungen anzufordern, die in den API-Ressourcen nicht definiert sind. Zum Beispiel kannst du die Berechtigung email
anfordern, auch wenn die API-Ressourcen die Berechtigung email
nicht verfügbar haben. Nicht verfügbare Berechtigungen werden sicher ignoriert.
Nach der erfolgreichen Anmeldung wird Logto die entsprechenden Berechtigungen an API-Ressourcen gemäß den Rollen des Benutzers ausstellen.
Zugangstoken für die API-Ressource abrufen
Um das Zugangstoken für eine spezifische API-Ressource abzurufen, kannst du die Methode getAccessToken
verwenden:
const accessToken = await logtoClient.getAccessToken('https://store.your-app.com/api');
console.log('Zugangstoken', accessToken);
Diese Methode gibt ein JWT-Zugangstoken zurück, das verwendet werden kann, um auf die API-Ressource zuzugreifen, wenn der Benutzer die entsprechenden Berechtigungen hat. Wenn das aktuell zwischengespeicherte Zugangstoken abgelaufen ist, versucht diese Methode automatisch, ein Auffrischungstoken zu verwenden, um ein neues Zugangstoken zu erhalten.
Organisationstokens abrufen
Wenn Organisationen neu für dich sind, lies bitte 🏢 Organisationen (Multi-Tenancy), um loszulegen.
Du musst die Berechtigung UserScope.Organizations
hinzufügen, wenn du den Logto-Client konfigurierst:
import LogtoClient, { UserScope } from '@logto/chrome-extension';
const logtoClient = new LogtoClient({
// ...other configs
scopes: [UserScope.Organizations],
});
Sobald der Benutzer angemeldet ist, kannst du das Organisationstoken für den Benutzer abrufen:
// Holen Sie sich organizationIds aus den userInfo
const claims = await logtoClient.getIdTokenClaims();
const organizationIds = claims.organizations;
/**
* Oder aus den ID-Token-Ansprüchen
*
* const claims = await logtoClient.getIdTokenClaims();
* const organizationIds = claims.organizations;
*/
// Holen Sie sich das Organisationstoken
if (organizationIds.length > 0) {
const organizationId = organizationIds[0];
const organizationAccessToken = await logtoClient.getOrganizationToken(organizationId);
console.log('Organisationstoken', organizationAccessToken);
}
./code/_scopes-and-claims-code.mdx./code/_config-organization-code.mdx
Zugangstoken an Anfrage-Header anhängen
Platziere das Token im Authorization
-Feld der HTTP-Header im Bearer-Format (Bearer YOUR_TOKEN
), und du bist startklar.
Der Integrationsablauf des Bearer-Tokens kann je nach verwendetem Framework oder Anfrager variieren. Wähle deinen eigenen Weg, um den Anfrage-Authorization
-Header anzuwenden.