Jeśli korzystasz z Uwierzytelniania Firebase za pomocą Identity Platform , możesz rozszerzyć Uwierzytelnianie Firebase, blokując Cloud Functions .
Funkcje blokowania umożliwiają wykonanie niestandardowego kodu, który modyfikuje wynik rejestracji użytkownika lub logowania się do Twojej aplikacji. Na przykład możesz uniemożliwić użytkownikowi uwierzytelnianie, jeśli nie spełnia określonych kryteriów, lub zaktualizować informacje użytkownika przed zwróceniem ich do aplikacji klienckiej.
Zanim zaczniesz
Aby korzystać z funkcji blokowania, musisz uaktualnić projekt Firebase do Uwierzytelniania Firebase za pomocą Identity Platform. Jeśli jeszcze nie uaktualniłeś, zrób to najpierw.
Zrozumienie funkcji blokujących
Możesz zarejestrować funkcje blokujące dla dwóch zdarzeń:
beforeCreate
: wyzwalane przed zapisaniem nowego użytkownika w bazie danych uwierzytelniania Firebase i przed zwróceniem tokena do aplikacji klienckiej.beforeSignIn
: wyzwalacze po zweryfikowaniu danych logowania użytkownika, ale zanim Uwierzytelnianie Firebase zwróci token identyfikatora do aplikacji klienckiej. Jeśli Twoja aplikacja korzysta z uwierzytelniania wieloskładnikowego, funkcja jest wyzwalana po zweryfikowaniu przez użytkownika drugiego czynnika. Zauważ, że utworzenie nowego użytkownika wyzwala równieżbeforeSignIn
, opróczbeforeCreate
.
Podczas korzystania z funkcji blokowania pamiętaj o następujących kwestiach:
Twoja funkcja musi odpowiedzieć w ciągu 7 sekund. Po 7 sekundach Uwierzytelnianie Firebase zwraca błąd, a operacja klienta kończy się niepowodzeniem.
Kody odpowiedzi HTTP inne niż
200
są przekazywane do aplikacji klienckich. Upewnij się, że Twój kod klienta obsługuje wszelkie błędy, które może zwrócić Twoja funkcja.Funkcje dotyczą wszystkich użytkowników w Twoim projekcie, w tym wszystkich użytkowników znajdujących się w dzierżawie . Uwierzytelnianie Firebase udostępnia informacje o użytkownikach Twojej funkcji, w tym o dzierżawcach, do których należą, dzięki czemu możesz odpowiednio zareagować.
Powiązanie innego dostawcy tożsamości z kontem ponownie wyzwala wszystkie zarejestrowane funkcje
beforeSignIn
.Uwierzytelnianie anonimowe i niestandardowe nie wyzwalają funkcji blokowania.
Wdróż i zarejestruj funkcję blokowania
Aby wstawić własny kod do przepływów uwierzytelniania użytkownika, wdróż i zarejestruj funkcje blokujące. Po wdrożeniu i zarejestrowaniu funkcji blokowania kod niestandardowy musi zostać pomyślnie ukończony, aby uwierzytelnianie i tworzenie użytkowników zakończyło się pomyślnie.
Wdróż funkcję blokowania
Funkcję blokującą wdraża się w taki sam sposób, jak każdą funkcję. (szczegółowe informacje można znaleźć na stronie Wprowadzenie do funkcji Cloud Functions). W podsumowaniu:
Napisz funkcje w chmurze, które obsługują zdarzenie
beforeCreate
, zdarzeniebeforeSignIn
lub oba te zdarzenia.Na przykład, aby rozpocząć, możesz dodać następujące funkcje no-op do
index.js
:const functions = require('firebase-functions'); exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => { // TODO }); exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => { // TODO });
W powyższych przykładach pominięto implementację niestandardowej logiki uwierzytelniania. Zobacz poniższe sekcje, aby dowiedzieć się, jak zaimplementować funkcje blokujące i typowe scenariusze, aby uzyskać konkretne przykłady.
Wdróż swoje funkcje za pomocą interfejsu wiersza polecenia Firebase:
firebase deploy --only functions
Musisz ponownie wdrażać swoje funkcje za każdym razem, gdy je aktualizujesz.
Zarejestruj funkcję blokowania
Przejdź do strony Ustawienia uwierzytelniania Firebase w konsoli Firebase.
Wybierz zakładkę Funkcje blokujące .
Zarejestruj funkcję blokowania, wybierając ją z menu rozwijanego przed utworzeniem konta (przed utworzeniem) lub przed zalogowaniem (przed logowaniem) .
Zapisz zmiany.
Uzyskiwanie informacji o użytkowniku i kontekście
beforeSignIn
i beforeCreate
udostępniają obiekty User
i EventContext
, które zawierają informacje o logowaniu użytkownika. Użyj tych wartości w kodzie, aby określić, czy zezwolić na kontynuowanie operacji.
Listę właściwości dostępnych w obiekcie User
można znaleźć w dokumentacji API UserRecord
.
Obiekt EventContext
zawiera następujące właściwości:
Nazwa | Opis | Przykład |
---|---|---|
locale | Ustawienia regionalne aplikacji. Ustawienia regionalne można ustawić za pomocą pakietu SDK klienta lub przez przekazanie nagłówka ustawień regionalnych w interfejsie API REST. | fr lub sv-SE |
ipAddress | Adres IP urządzenia, z którego rejestruje się lub loguje użytkownik końcowy. | 114.14.200.1 |
userAgent | Klient użytkownika uruchamiający funkcję blokowania. | Mozilla/5.0 (X11; Linux x86_64) |
eventId | Unikalny identyfikator wydarzenia. | rWsyPtolplG2TBFoOkkgyg |
eventType | Typ zdarzenia. Zawiera informacje o nazwie zdarzenia, na przykład beforeSignIn lub beforeCreate , oraz powiązanej używanej metodzie logowania, takiej jak Google lub adres e-mail/hasło. | providers/cloud.auth/eventTypes/user.beforeSignIn:password |
authType | Zawsze USER . | USER |
resource | Projekt lub dzierżawa Firebase Authentication. | projects/ project-id /tenants/ tenant-id |
timestamp | Czas wyzwolenia zdarzenia, sformatowany jako ciąg RFC 3339 . | Tue, 23 Jul 2019 21:10:57 GMT |
additionalUserInfo | Obiekt zawierający informacje o użytkowniku. | AdditionalUserInfo |
credential | Obiekt zawierający informacje o poświadczeniach użytkownika. | AuthCredential |
Blokowanie rejestracji lub logowania
Aby zablokować próbę rejestracji lub logowania, zgłoś HttpsError
w swojej funkcji. Na przykład:
Node.js
throw new functions.auth.HttpsError('permission-denied');
W poniższej tabeli wymieniono błędy, które możesz zgłosić, wraz z ich domyślnym komunikatem o błędzie:
Nazwa | Kod | Wiadomość |
---|---|---|
invalid-argument | 400 | Klient podał nieprawidłowy argument. |
failed-precondition | 400 | Żądanie nie może zostać wykonane w obecnym stanie systemu. |
out-of-range | 400 | Klient określił nieprawidłowy zakres. |
unauthenticated | 401 | Brakujący, nieprawidłowy lub wygasły token OAuth. |
permission-denied | 403 | Klient nie posiada wystarczających uprawnień. |
not-found | 404 | Nie znaleziono określonego zasobu. |
aborted | 409 | Konflikt współbieżności, taki jak konflikt odczytu, modyfikacji i zapisu. |
already-exists | 409 | Zasób, który klient próbował utworzyć, już istnieje. |
resource-exhausted | 429 | Brak przydziału zasobów lub osiągnięcie ograniczenia szybkości. |
cancelled | 499 | Żądanie anulowane przez klienta. |
data-loss | 500 | Nieodwracalna utrata lub uszkodzenie danych. |
unknown | 500 | Nieznany błąd serwera. |
internal | 500 | Wewnętrzny błąd serwera. |
not-implemented | 501 | Metoda API nie zaimplementowana przez serwer. |
unavailable | 503 | Serwis niedostępny. |
deadline-exceeded | 504 | Przekroczono termin żądania. |
Możesz także określić niestandardowy komunikat o błędzie:
Node.js
throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');
Poniższy przykład pokazuje, jak zablokować użytkownikom spoza określonej domeny możliwość rejestracji w Twojej aplikacji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
// (If the user is authenticating within a tenant context, the tenant ID can be determined from
// user.tenantId or from context.resource, e.g. 'projects/project-id/tenant/tenant-id-1')
// Only users of a specific domain can sign up.
if (user.email.indexOf('@acme.com') === -1) {
throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`);
}
});
Niezależnie od tego, czy używasz wiadomości domyślnej, czy niestandardowej, Cloud Functions zawija błąd i zwraca go klientowi jako błąd wewnętrzny. Na przykład:
throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);
Twoja aplikacja powinna wykryć błąd i odpowiednio go obsłużyć. Na przykład:
JavaScript
// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
firebase.auth().createUserWithEmailAndPassword('johndoe@example.com', 'password')
.then((result) => {
result.user.getIdTokenResult()
})
.then((idTokenResult) => {
console.log(idTokenResult.claim.admin);
})
.catch((error) => {
if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
// Display error.
} else {
// Registration succeeds.
}
});
Modyfikowanie użytkownika
Zamiast blokować rejestrację lub próbę logowania, można zezwolić na kontynuowanie operacji, ale zmodyfikować obiekt User
, który jest zapisywany w bazie danych uwierzytelniania Firebase i zwracany do klienta.
Aby zmodyfikować użytkownika, zwróć obiekt z programu obsługi zdarzeń zawierający pola do zmodyfikowania. Możesz modyfikować następujące pola:
-
displayName
-
disabled
-
emailVerified
-
photoURL
-
customClaims
-
sessionClaims
(tylkobeforeSignIn
)
Z wyjątkiem sessionClaims
, wszystkie zmodyfikowane pola są zapisywane w bazie danych Firebase Authentication, co oznacza, że są uwzględniane w tokenie odpowiedzi i utrzymują się między sesjami użytkownika.
Poniższy przykład pokazuje, jak ustawić domyślną nazwę wyświetlaną:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
return {
// If no display name is provided, set it to "Guest".
displayName: user.displayName || 'Guest';
};
});
Jeśli zarejestrujesz procedurę obsługi zdarzeń dla beforeCreate
i beforeSignIn
, pamiętaj, że beforeSignIn
wykonuje się po beforeCreate
. Pola użytkownika zaktualizowane w beforeCreate
są widoczne w beforeSignIn
. Jeśli ustawisz pole inne niż sessionClaims
w obu programach obsługi zdarzeń, wartość ustawiona w beforeSignIn
zastępuje wartość ustawioną w beforeCreate
. Tylko w przypadku sessionClaims
są one propagowane do oświadczeń tokenów bieżącej sesji, ale nie są utrwalane ani przechowywane w bazie danych.
Na przykład, jeśli ustawiono jakiekolwiek sessionClaims
, beforeSignIn
zwróci je z dowolnymi roszczeniami beforeCreate
i zostaną scalone. Po scaleniu, jeśli klucz sessionClaims
pasuje do klucza w customClaims
, pasujące customClaims
zostaną zastąpione w oświadczeniach tokenu przez klucz sessionClaims
. Jednak nadpisany klucz customClaims
będzie nadal utrwalany w bazie danych dla przyszłych żądań.
Obsługiwane dane uwierzytelniające i dane OAuth
Możesz przekazywać poświadczenia i dane OAuth do funkcji blokujących od różnych dostawców tożsamości. W poniższej tabeli przedstawiono, jakie poświadczenia i dane są obsługiwane dla każdego dostawcy tożsamości:
Dostawca tożsamości | Token identyfikacyjny | Token dostępu | Data ważności | Sekret tokena | Odśwież token | Roszczenia dotyczące logowania |
---|---|---|---|---|---|---|
TAk | TAk | TAk | Nie | TAk | Nie | |
Nie | TAk | TAk | Nie | Nie | Nie | |
Świergot | Nie | TAk | Nie | TAk | Nie | Nie |
GitHub | Nie | TAk | Nie | Nie | Nie | Nie |
Microsoft | TAk | TAk | TAk | Nie | TAk | Nie |
Nie | TAk | TAk | Nie | Nie | Nie | |
Wieśniak | TAk | TAk | TAk | Nie | TAk | Nie |
Jabłko | TAk | TAk | TAk | Nie | TAk | Nie |
SAML | Nie | Nie | Nie | Nie | Nie | TAk |
OIDC | TAk | TAk | TAk | Nie | TAk | TAk |
Odśwież tokeny
Aby użyć tokena odświeżania w funkcji blokowania, musisz najpierw zaznaczyć pole wyboru na stronie Funkcje blokowania konsoli Firebase.
Tokeny odświeżania nie będą zwracane przez dostawców tożsamości podczas bezpośredniego logowania przy użyciu poświadczeń OAuth, takich jak token identyfikatora lub token dostępu. W takiej sytuacji te same poświadczenia OAuth po stronie klienta zostaną przekazane do funkcji blokującej.
W poniższych sekcjach opisano każdy typ dostawcy tożsamości oraz obsługiwane poświadczenia i dane.
Dostawcy generyczni OIDC
Gdy użytkownik zaloguje się za pomocą ogólnego dostawcy OIDC, zostaną przekazane następujące poświadczenia:
- Token identyfikatora : podawany, jeśli wybrano przepływ
id_token
. - Token dostępu : udostępniany, jeśli wybrano przepływ kodu. Pamiętaj, że przepływ kodu jest obecnie obsługiwany tylko za pośrednictwem interfejsu API REST.
- Odśwież token : podana, jeśli wybrano zakres
offline_access
.
Przykład:
const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
Gdy użytkownik zaloguje się w Google, zostaną przekazane następujące dane uwierzytelniające:
- Token identyfikacyjny
- Token dostępu
- Odśwież token : udostępniany tylko wtedy, gdy zażądano następujących parametrów niestandardowych:
-
access_type=offline
-
prompt=consent
, jeśli użytkownik wcześniej wyraził zgodę i nie zażądano nowego zakresu
-
Przykład:
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
'access_type': 'offline',
'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);
Dowiedz się więcej o tokenach odświeżania Google .
Gdy użytkownik zaloguje się za pomocą Facebooka, przekazane zostaną następujące poświadczenia:
- Token dostępu : zwracany jest token dostępu, który można wymienić na inny token dostępu. Dowiedz się więcej o różnych typach tokenów dostępu obsługiwanych przez Facebook i o tym, jak możesz je wymienić na tokeny o długim czasie życia .
GitHub
Gdy użytkownik zaloguje się za pomocą GitHub, zostaną przekazane następujące poświadczenia:
- Token dostępu : nie wygasa, chyba że zostanie unieważniony.
Microsoft
Gdy użytkownik zaloguje się za pomocą firmy Microsoft, zostaną przekazane następujące poświadczenia:
- Token identyfikacyjny
- Token dostępu
- Odśwież token : Przekazywany do funkcji blokującej, jeśli wybrano zakres
offline_access
.
Przykład:
const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
Wieśniak
Gdy użytkownik zaloguje się za pomocą Yahoo, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:
- Token identyfikacyjny
- Token dostępu
- Odśwież token
Gdy użytkownik zaloguje się na LinkedIn, zostaną przekazane następujące poświadczenia:
- Token dostępu
Jabłko
Gdy użytkownik zaloguje się za pomocą Apple, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:
- Token identyfikacyjny
- Token dostępu
- Odśwież token
Typowe scenariusze
Poniższe przykłady pokazują kilka typowych przypadków użycia funkcji blokowania:
Zezwalanie na rejestrację tylko z określonej domeny
Poniższy przykład pokazuje, jak uniemożliwić użytkownikom, którzy nie należą do domeny example.com
, rejestrację w Twojej aplikacji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (!user.email || user.email.indexOf('@example.com') === -1) {
throw new functions.auth.HttpsError(
'invalid-argument', `Unauthorized email "${user.email}"`);
}
});
Blokowanie rejestracji użytkownikom z niezweryfikowanymi wiadomościami e-mail
Poniższy przykład pokazuje, jak uniemożliwić użytkownikom z niezweryfikowanymi adresami e-mail rejestrowanie się w Twojej aplikacji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.email && !user.emailVerified) {
throw new functions.auth.HttpsError(
'invalid-argument', `Unverified email "${user.email}"`);
}
});
Wymaganie weryfikacji adresu e-mail przy rejestracji
Poniższy przykład pokazuje, jak wymagać od użytkownika weryfikacji adresu e-mail po rejestracji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
const locale = context.locale;
if (user.email && !user.emailVerified) {
// Send custom email verification on sign-up.
return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
return sendCustomVerificationEmail(user.email, link, locale);
});
}
});
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
if (user.email && !user.emailVerified) {
throw new functions.auth.HttpsError(
'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
}
});
Traktowanie niektórych wiadomości e-mail od dostawców tożsamości jako zweryfikowanych
Poniższy przykład pokazuje, jak traktować wiadomości e-mail użytkowników od niektórych dostawców tożsamości jako zweryfikowane:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
return {
emailVerified: true,
};
}
});
Blokowanie logowania z niektórych adresów IP
Poniższy przykład blokuje logowanie z określonych zakresów adresów IP:
Node.js
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
if (isSuspiciousIpAddress(context.ipAddress)) {
throw new functions.auth.HttpsError(
'permission-denied', 'Unauthorized access!');
}
});
Ustawianie oświadczeń niestandardowych i sesji
Poniższy przykład pokazuje, jak ustawić oświadczenia niestandardowe i sesyjne:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (context.credential &&
context.credential.providerId === 'saml.my-provider-id') {
return {
// Employee ID does not change so save in persistent claims (stored in
// Auth DB).
customClaims: {
eid: context.credential.claims.employeeid,
},
// Copy role and groups to token claims. These will not be persisted.
sessionClaims: {
role: context.credential.claims.role,
groups: context.credential.claims.groups,
}
}
}
});
Śledzenie adresów IP w celu monitorowania podejrzanej aktywności
Możesz zapobiec kradzieży tokenów, śledząc adres IP, z którego loguje się użytkownik, i porównując go z adresem IP przy kolejnych żądaniach. Jeśli żądanie wydaje się podejrzane — na przykład adresy IP pochodzą z różnych regionów geograficznych — możesz poprosić użytkownika o ponowne zalogowanie się.
Użyj oświadczeń sesji, aby śledzić adres IP, za pomocą którego loguje się użytkownik:
Node.js
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => { return { sessionClaims: { signInIpAddress: context.ipAddress, }, }; });
Gdy użytkownik próbuje uzyskać dostęp do zasobów wymagających uwierzytelnienia za pomocą Uwierzytelniania Firebase, porównaj adres IP w żądaniu z adresem IP użytym do logowania:
Node.js
app.post('/getRestrictedData', (req, res) => { // Get the ID token passed. const idToken = req.body.idToken; // Verify the ID token, check if revoked and decode its payload. admin.auth().verifyIdToken(idToken, true).then((claims) => { // Get request IP address const requestIpAddress = req.connection.remoteAddress; // Get sign-in IP address. const signInIpAddress = claims.signInIpAddress; // Check if the request IP address origin is suspicious relative to // the session IP addresses. The current request timestamp and the // auth_time of the ID token can provide additional signals of abuse, // especially if the IP address suddenly changed. If there was a sudden // geographical change in a short period of time, then it will give // stronger signals of possible abuse. if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) { // Suspicious IP address change. Require re-authentication. // You can also revoke all user sessions by calling: // admin.auth().revokeRefreshTokens(claims.sub). res.status(401).send({error: 'Unauthorized access. Please login again!'}); } else { // Access is valid. Try to return data. getData(claims).then(data => { res.end(JSON.stringify(data); }, error => { res.status(500).send({ error: 'Server error!' }) }); } }); });
Przeglądanie zdjęć użytkowników
Poniższy przykład pokazuje, jak oczyścić zdjęcia profilowe użytkowników:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.photoURL) {
return isPhotoAppropriate(user.photoURL)
.then((status) => {
if (!status) {
// Sanitize inappropriate photos by replacing them with guest photos.
// Users could also be blocked from sign-up, disabled, etc.
return {
photoURL: PLACEHOLDER_GUEST_PHOTO_URL,
};
}
});
});
Aby dowiedzieć się więcej o wykrywaniu i oczyszczaniu obrazów, zapoznaj się z dokumentacją Cloud Vision .
Uzyskiwanie dostępu do danych uwierzytelniających OAuth dostawcy tożsamości użytkownika
Poniższy przykład pokazuje, jak uzyskać token odświeżania dla użytkownika, który zalogował się w Google, i użyć go do wywołania interfejsów API Kalendarza Google. Token odświeżania jest przechowywany w trybie offline.
Node.js
const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
keys.web.client_id,
keys.web.client_secret
);
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (context.credential &&
context.credential.providerId === 'google.com') {
// Store the refresh token for later offline use.
// These will only be returned if refresh tokens credentials are included
// (enabled by Cloud console).
return saveUserRefreshToken(
user.uid,
context.credential.refreshToken,
'google.com'
)
.then(() => {
// Blocking the function is not required. The function can resolve while
// this operation continues to run in the background.
return new Promise((resolve, reject) => {
// For this operation to succeed, the appropriate OAuth scope should be requested
// on sign in with Google, client-side. In this case:
// https://www.googleapis.com/auth/calendar
// You can check granted_scopes from within:
// context.additionalUserInfo.profile.granted_scopes (space joined list of scopes).
// Set access token/refresh token.
oAuth2Client.setCredentials({
access_token: context.credential.accessToken,
refresh_token: context.credential.refreshToken,
});
const calendar = google.calendar('v3');
// Setup Onboarding event on user's calendar.
const event = {/** ... */};
calendar.events.insert({
auth: oauth2client,
calendarId: 'primary',
resource: event,
}, (err, event) => {
// Do not fail. This is a best effort approach.
resolve();
});
});
})
}
});
Jeśli korzystasz z Uwierzytelniania Firebase za pomocą Identity Platform , możesz rozszerzyć Uwierzytelnianie Firebase, blokując Cloud Functions .
Funkcje blokowania umożliwiają wykonanie niestandardowego kodu, który modyfikuje wynik rejestracji użytkownika lub logowania się do Twojej aplikacji. Na przykład możesz uniemożliwić użytkownikowi uwierzytelnianie, jeśli nie spełnia określonych kryteriów, lub zaktualizować informacje użytkownika przed zwróceniem ich do aplikacji klienckiej.
Zanim zaczniesz
Aby korzystać z funkcji blokowania, musisz uaktualnić projekt Firebase do Uwierzytelniania Firebase za pomocą Identity Platform. Jeśli jeszcze nie uaktualniłeś, zrób to najpierw.
Zrozumienie funkcji blokujących
Możesz zarejestrować funkcje blokujące dla dwóch zdarzeń:
beforeCreate
: wyzwalane przed zapisaniem nowego użytkownika w bazie danych uwierzytelniania Firebase i przed zwróceniem tokena do aplikacji klienckiej.beforeSignIn
: wyzwalacze po zweryfikowaniu danych logowania użytkownika, ale zanim Uwierzytelnianie Firebase zwróci token identyfikatora do aplikacji klienckiej. Jeśli Twoja aplikacja korzysta z uwierzytelniania wieloskładnikowego, funkcja jest wyzwalana po zweryfikowaniu przez użytkownika drugiego czynnika. Zauważ, że utworzenie nowego użytkownika wyzwala równieżbeforeSignIn
, opróczbeforeCreate
.
Podczas korzystania z funkcji blokowania pamiętaj o następujących kwestiach:
Twoja funkcja musi odpowiedzieć w ciągu 7 sekund. Po 7 sekundach Uwierzytelnianie Firebase zwraca błąd, a operacja klienta kończy się niepowodzeniem.
Kody odpowiedzi HTTP inne niż
200
są przekazywane do aplikacji klienckich. Upewnij się, że Twój kod klienta obsługuje wszelkie błędy, które może zwrócić Twoja funkcja.Funkcje dotyczą wszystkich użytkowników w Twoim projekcie, w tym wszystkich użytkowników znajdujących się w dzierżawie . Uwierzytelnianie Firebase udostępnia informacje o użytkownikach Twojej funkcji, w tym o dzierżawcach, do których należą, dzięki czemu możesz odpowiednio zareagować.
Powiązanie innego dostawcy tożsamości z kontem ponownie wyzwala wszystkie zarejestrowane funkcje
beforeSignIn
.Uwierzytelnianie anonimowe i niestandardowe nie wyzwalają funkcji blokowania.
Wdróż i zarejestruj funkcję blokowania
Aby wstawić własny kod do przepływów uwierzytelniania użytkownika, wdróż i zarejestruj funkcje blokujące. Po wdrożeniu i zarejestrowaniu funkcji blokowania kod niestandardowy musi zostać pomyślnie ukończony, aby uwierzytelnianie i tworzenie użytkowników zakończyło się pomyślnie.
Wdróż funkcję blokowania
Funkcję blokującą wdraża się w taki sam sposób, jak każdą funkcję. (szczegółowe informacje można znaleźć na stronie Wprowadzenie do funkcji Cloud Functions). W podsumowaniu:
Napisz funkcje w chmurze, które obsługują zdarzenie
beforeCreate
, zdarzeniebeforeSignIn
lub oba te zdarzenia.Na przykład, aby rozpocząć, możesz dodać następujące funkcje no-op do
index.js
:const functions = require('firebase-functions'); exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => { // TODO }); exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => { // TODO });
W powyższych przykładach pominięto implementację niestandardowej logiki uwierzytelniania. Zobacz poniższe sekcje, aby dowiedzieć się, jak zaimplementować funkcje blokujące i typowe scenariusze, aby uzyskać konkretne przykłady.
Wdróż swoje funkcje za pomocą interfejsu wiersza polecenia Firebase:
firebase deploy --only functions
Musisz ponownie wdrażać swoje funkcje za każdym razem, gdy je aktualizujesz.
Zarejestruj funkcję blokowania
Przejdź do strony Ustawienia uwierzytelniania Firebase w konsoli Firebase.
Wybierz zakładkę Funkcje blokujące .
Zarejestruj funkcję blokowania, wybierając ją z menu rozwijanego przed utworzeniem konta (przed utworzeniem) lub przed zalogowaniem (przed logowaniem) .
Zapisz zmiany.
Uzyskiwanie informacji o użytkowniku i kontekście
beforeSignIn
i beforeCreate
udostępniają obiekty User
i EventContext
, które zawierają informacje o logowaniu użytkownika. Użyj tych wartości w kodzie, aby określić, czy zezwolić na kontynuowanie operacji.
Listę właściwości dostępnych w obiekcie User
można znaleźć w dokumentacji API UserRecord
.
Obiekt EventContext
zawiera następujące właściwości:
Nazwa | Opis | Przykład |
---|---|---|
locale | Ustawienia regionalne aplikacji. Ustawienia regionalne można ustawić za pomocą pakietu SDK klienta lub przez przekazanie nagłówka ustawień regionalnych w interfejsie API REST. | fr lub sv-SE |
ipAddress | Adres IP urządzenia, z którego rejestruje się lub loguje użytkownik końcowy. | 114.14.200.1 |
userAgent | Klient użytkownika uruchamiający funkcję blokowania. | Mozilla/5.0 (X11; Linux x86_64) |
eventId | Unikalny identyfikator wydarzenia. | rWsyPtolplG2TBFoOkkgyg |
eventType | Typ zdarzenia. Zawiera informacje o nazwie zdarzenia, na przykład beforeSignIn lub beforeCreate , oraz powiązanej używanej metodzie logowania, takiej jak Google lub adres e-mail/hasło. | providers/cloud.auth/eventTypes/user.beforeSignIn:password |
authType | Zawsze USER . | USER |
resource | Projekt lub dzierżawa Firebase Authentication. | projects/ project-id /tenants/ tenant-id |
timestamp | Czas wyzwolenia zdarzenia, sformatowany jako ciąg RFC 3339 . | Tue, 23 Jul 2019 21:10:57 GMT |
additionalUserInfo | Obiekt zawierający informacje o użytkowniku. | AdditionalUserInfo |
credential | Obiekt zawierający informacje o poświadczeniach użytkownika. | AuthCredential |
Blokowanie rejestracji lub logowania
Aby zablokować próbę rejestracji lub logowania, zgłoś HttpsError
w swojej funkcji. Na przykład:
Node.js
throw new functions.auth.HttpsError('permission-denied');
W poniższej tabeli wymieniono błędy, które możesz zgłosić, wraz z ich domyślnym komunikatem o błędzie:
Nazwa | Kod | Wiadomość |
---|---|---|
invalid-argument | 400 | Klient podał nieprawidłowy argument. |
failed-precondition | 400 | Żądanie nie może zostać wykonane w obecnym stanie systemu. |
out-of-range | 400 | Klient określił nieprawidłowy zakres. |
unauthenticated | 401 | Brakujący, nieprawidłowy lub wygasły token OAuth. |
permission-denied | 403 | Klient nie posiada wystarczających uprawnień. |
not-found | 404 | Nie znaleziono określonego zasobu. |
aborted | 409 | Konflikt współbieżności, taki jak konflikt odczytu, modyfikacji i zapisu. |
already-exists | 409 | Zasób, który klient próbował utworzyć, już istnieje. |
resource-exhausted | 429 | Brak przydziału zasobów lub osiągnięcie ograniczenia szybkości. |
cancelled | 499 | Żądanie anulowane przez klienta. |
data-loss | 500 | Nieodwracalna utrata lub uszkodzenie danych. |
unknown | 500 | Nieznany błąd serwera. |
internal | 500 | Wewnętrzny błąd serwera. |
not-implemented | 501 | Metoda API nie zaimplementowana przez serwer. |
unavailable | 503 | Serwis niedostępny. |
deadline-exceeded | 504 | Przekroczono termin żądania. |
Możesz również określić niestandardowy komunikat o błędzie:
Node.js
throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');
Poniższy przykład pokazuje, jak zablokować użytkownikom spoza określonej domeny możliwość rejestracji w Twojej aplikacji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
// (If the user is authenticating within a tenant context, the tenant ID can be determined from
// user.tenantId or from context.resource, e.g. 'projects/project-id/tenant/tenant-id-1')
// Only users of a specific domain can sign up.
if (user.email.indexOf('@acme.com') === -1) {
throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`);
}
});
Niezależnie od tego, czy używasz wiadomości domyślnej, czy niestandardowej, Cloud Functions zawija błąd i zwraca go klientowi jako błąd wewnętrzny. Na przykład:
throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);
Twoja aplikacja powinna wykryć błąd i odpowiednio go obsłużyć. Na przykład:
JavaScript
// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
firebase.auth().createUserWithEmailAndPassword('johndoe@example.com', 'password')
.then((result) => {
result.user.getIdTokenResult()
})
.then((idTokenResult) => {
console.log(idTokenResult.claim.admin);
})
.catch((error) => {
if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
// Display error.
} else {
// Registration succeeds.
}
});
Modyfikowanie użytkownika
Zamiast blokować rejestrację lub próbę logowania, można zezwolić na kontynuowanie operacji, ale zmodyfikować obiekt User
, który jest zapisywany w bazie danych uwierzytelniania Firebase i zwracany do klienta.
Aby zmodyfikować użytkownika, zwróć obiekt z programu obsługi zdarzeń zawierający pola do zmodyfikowania. Możesz modyfikować następujące pola:
-
displayName
-
disabled
-
emailVerified
-
photoURL
-
customClaims
-
sessionClaims
(tylkobeforeSignIn
)
Z wyjątkiem sessionClaims
, wszystkie zmodyfikowane pola są zapisywane w bazie danych Firebase Authentication, co oznacza, że są uwzględniane w tokenie odpowiedzi i utrzymują się między sesjami użytkownika.
Poniższy przykład pokazuje, jak ustawić domyślną nazwę wyświetlaną:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
return {
// If no display name is provided, set it to "Guest".
displayName: user.displayName || 'Guest';
};
});
Jeśli zarejestrujesz procedurę obsługi zdarzeń dla beforeCreate
i beforeSignIn
, pamiętaj, że beforeSignIn
wykonuje się po beforeCreate
. Pola użytkownika zaktualizowane w beforeCreate
są widoczne w beforeSignIn
. Jeśli ustawisz pole inne niż sessionClaims
w obu programach obsługi zdarzeń, wartość ustawiona w beforeSignIn
zastępuje wartość ustawioną w beforeCreate
. Tylko w przypadku sessionClaims
są one propagowane do oświadczeń tokenów bieżącej sesji, ale nie są utrwalane ani przechowywane w bazie danych.
Na przykład, jeśli ustawiono jakiekolwiek sessionClaims
, beforeSignIn
zwróci je z dowolnymi roszczeniami beforeCreate
i zostaną scalone. Po scaleniu, jeśli klucz sessionClaims
pasuje do klucza w customClaims
, pasujące customClaims
zostaną zastąpione w oświadczeniach tokenu przez klucz sessionClaims
. Jednak nadpisany klucz customClaims
będzie nadal utrwalany w bazie danych dla przyszłych żądań.
Obsługiwane dane uwierzytelniające i dane OAuth
Możesz przekazywać poświadczenia i dane OAuth do funkcji blokujących od różnych dostawców tożsamości. W poniższej tabeli przedstawiono, jakie poświadczenia i dane są obsługiwane dla każdego dostawcy tożsamości:
Dostawca tożsamości | Token identyfikacyjny | Token dostępu | Data ważności | Sekret tokena | Odśwież token | Roszczenia dotyczące logowania |
---|---|---|---|---|---|---|
TAk | TAk | TAk | Nie | TAk | Nie | |
Nie | TAk | TAk | Nie | Nie | Nie | |
Świergot | Nie | TAk | Nie | TAk | Nie | Nie |
GitHub | Nie | TAk | Nie | Nie | Nie | Nie |
Microsoft | TAk | TAk | TAk | Nie | TAk | Nie |
Nie | TAk | TAk | Nie | Nie | Nie | |
Wieśniak | TAk | TAk | TAk | Nie | TAk | Nie |
Jabłko | TAk | TAk | TAk | Nie | TAk | Nie |
SAML | Nie | Nie | Nie | Nie | Nie | TAk |
OIDC | TAk | TAk | TAk | Nie | TAk | TAk |
Odśwież tokeny
Aby użyć tokena odświeżania w funkcji blokowania, musisz najpierw zaznaczyć pole wyboru na stronie Funkcje blokowania konsoli Firebase.
Tokeny odświeżania nie będą zwracane przez dostawców tożsamości podczas bezpośredniego logowania przy użyciu poświadczeń OAuth, takich jak token identyfikatora lub token dostępu. W takiej sytuacji te same poświadczenia OAuth po stronie klienta zostaną przekazane do funkcji blokującej.
W poniższych sekcjach opisano każdy typ dostawcy tożsamości oraz obsługiwane poświadczenia i dane.
Dostawcy generyczni OIDC
Gdy użytkownik zaloguje się za pomocą ogólnego dostawcy OIDC, zostaną przekazane następujące poświadczenia:
- Token identyfikatora : podawany, jeśli wybrano przepływ
id_token
. - Token dostępu : udostępniany, jeśli wybrano przepływ kodu. Pamiętaj, że przepływ kodu jest obecnie obsługiwany tylko za pośrednictwem interfejsu API REST.
- Odśwież token : podana, jeśli wybrano zakres
offline_access
.
Przykład:
const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
Gdy użytkownik zaloguje się w Google, zostaną przekazane następujące dane uwierzytelniające:
- Token identyfikacyjny
- Token dostępu
- Odśwież token : udostępniany tylko wtedy, gdy zażądano następujących parametrów niestandardowych:
-
access_type=offline
-
prompt=consent
, jeśli użytkownik wcześniej wyraził zgodę i nie zażądano nowego zakresu
-
Przykład:
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
'access_type': 'offline',
'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);
Dowiedz się więcej o tokenach odświeżania Google .
Gdy użytkownik zaloguje się za pomocą Facebooka, przekazane zostaną następujące poświadczenia:
- Token dostępu : zwracany jest token dostępu, który można wymienić na inny token dostępu. Dowiedz się więcej o różnych typach tokenów dostępu obsługiwanych przez Facebook i o tym, jak możesz je wymienić na tokeny o długim czasie życia .
GitHub
Gdy użytkownik zaloguje się za pomocą GitHub, zostaną przekazane następujące poświadczenia:
- Token dostępu : nie wygasa, chyba że zostanie unieważniony.
Microsoft
Gdy użytkownik zaloguje się za pomocą firmy Microsoft, zostaną przekazane następujące poświadczenia:
- Token identyfikacyjny
- Token dostępu
- Odśwież token : Przekazywany do funkcji blokującej, jeśli wybrano zakres
offline_access
.
Przykład:
const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
Wieśniak
Gdy użytkownik zaloguje się za pomocą Yahoo, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:
- Token identyfikacyjny
- Token dostępu
- Odśwież token
Gdy użytkownik zaloguje się na LinkedIn, zostaną przekazane następujące poświadczenia:
- Token dostępu
Jabłko
Gdy użytkownik zaloguje się za pomocą Apple, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:
- Token identyfikacyjny
- Token dostępu
- Odśwież token
Typowe scenariusze
Poniższe przykłady pokazują kilka typowych przypadków użycia funkcji blokowania:
Zezwalanie na rejestrację tylko z określonej domeny
Poniższy przykład pokazuje, jak uniemożliwić użytkownikom, którzy nie należą do domeny example.com
, rejestrację w Twojej aplikacji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (!user.email || user.email.indexOf('@example.com') === -1) {
throw new functions.auth.HttpsError(
'invalid-argument', `Unauthorized email "${user.email}"`);
}
});
Blokowanie rejestracji użytkownikom z niezweryfikowanymi wiadomościami e-mail
Poniższy przykład pokazuje, jak uniemożliwić użytkownikom z niezweryfikowanymi adresami e-mail rejestrowanie się w Twojej aplikacji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.email && !user.emailVerified) {
throw new functions.auth.HttpsError(
'invalid-argument', `Unverified email "${user.email}"`);
}
});
Wymaganie weryfikacji adresu e-mail przy rejestracji
Poniższy przykład pokazuje, jak wymagać od użytkownika weryfikacji adresu e-mail po rejestracji:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
const locale = context.locale;
if (user.email && !user.emailVerified) {
// Send custom email verification on sign-up.
return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
return sendCustomVerificationEmail(user.email, link, locale);
});
}
});
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
if (user.email && !user.emailVerified) {
throw new functions.auth.HttpsError(
'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
}
});
Traktowanie niektórych wiadomości e-mail od dostawców tożsamości jako zweryfikowanych
Poniższy przykład pokazuje, jak traktować wiadomości e-mail użytkowników od niektórych dostawców tożsamości jako zweryfikowane:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
return {
emailVerified: true,
};
}
});
Blokowanie logowania z niektórych adresów IP
Poniższy przykład blokuje logowanie z określonych zakresów adresów IP:
Node.js
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
if (isSuspiciousIpAddress(context.ipAddress)) {
throw new functions.auth.HttpsError(
'permission-denied', 'Unauthorized access!');
}
});
Ustawianie oświadczeń niestandardowych i sesji
Poniższy przykład pokazuje, jak ustawić oświadczenia niestandardowe i sesyjne:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (context.credential &&
context.credential.providerId === 'saml.my-provider-id') {
return {
// Employee ID does not change so save in persistent claims (stored in
// Auth DB).
customClaims: {
eid: context.credential.claims.employeeid,
},
// Copy role and groups to token claims. These will not be persisted.
sessionClaims: {
role: context.credential.claims.role,
groups: context.credential.claims.groups,
}
}
}
});
Śledzenie adresów IP w celu monitorowania podejrzanej aktywności
Możesz zapobiec kradzieży tokenów, śledząc adres IP, z którego loguje się użytkownik, i porównując go z adresem IP przy kolejnych żądaniach. Jeśli żądanie wydaje się podejrzane — na przykład adresy IP pochodzą z różnych regionów geograficznych — możesz poprosić użytkownika o ponowne zalogowanie się.
Użyj oświadczeń sesji, aby śledzić adres IP, za pomocą którego loguje się użytkownik:
Node.js
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => { return { sessionClaims: { signInIpAddress: context.ipAddress, }, }; });
Gdy użytkownik próbuje uzyskać dostęp do zasobów wymagających uwierzytelnienia za pomocą Uwierzytelniania Firebase, porównaj adres IP w żądaniu z adresem IP użytym do logowania:
Node.js
app.post('/getRestrictedData', (req, res) => { // Get the ID token passed. const idToken = req.body.idToken; // Verify the ID token, check if revoked and decode its payload. admin.auth().verifyIdToken(idToken, true).then((claims) => { // Get request IP address const requestIpAddress = req.connection.remoteAddress; // Get sign-in IP address. const signInIpAddress = claims.signInIpAddress; // Check if the request IP address origin is suspicious relative to // the session IP addresses. The current request timestamp and the // auth_time of the ID token can provide additional signals of abuse, // especially if the IP address suddenly changed. If there was a sudden // geographical change in a short period of time, then it will give // stronger signals of possible abuse. if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) { // Suspicious IP address change. Require re-authentication. // You can also revoke all user sessions by calling: // admin.auth().revokeRefreshTokens(claims.sub). res.status(401).send({error: 'Unauthorized access. Please login again!'}); } else { // Access is valid. Try to return data. getData(claims).then(data => { res.end(JSON.stringify(data); }, error => { res.status(500).send({ error: 'Server error!' }) }); } }); });
Przeglądanie zdjęć użytkowników
Poniższy przykład pokazuje, jak oczyścić zdjęcia profilowe użytkowników:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.photoURL) {
return isPhotoAppropriate(user.photoURL)
.then((status) => {
if (!status) {
// Sanitize inappropriate photos by replacing them with guest photos.
// Users could also be blocked from sign-up, disabled, etc.
return {
photoURL: PLACEHOLDER_GUEST_PHOTO_URL,
};
}
});
});
Aby dowiedzieć się więcej o wykrywaniu i oczyszczaniu obrazów, zapoznaj się z dokumentacją Cloud Vision .
Uzyskiwanie dostępu do danych uwierzytelniających OAuth dostawcy tożsamości użytkownika
Poniższy przykład pokazuje, jak uzyskać token odświeżania dla użytkownika, który zalogował się w Google, i użyć go do wywołania interfejsów API Kalendarza Google. Token odświeżania jest przechowywany w celu uzyskania dostępu w trybie offline.
Node.js
const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
keys.web.client_id,
keys.web.client_secret
);
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (context.credential &&
context.credential.providerId === 'google.com') {
// Store the refresh token for later offline use.
// These will only be returned if refresh tokens credentials are included
// (enabled by Cloud console).
return saveUserRefreshToken(
user.uid,
context.credential.refreshToken,
'google.com'
)
.then(() => {
// Blocking the function is not required. The function can resolve while
// this operation continues to run in the background.
return new Promise((resolve, reject) => {
// For this operation to succeed, the appropriate OAuth scope should be requested
// on sign in with Google, client-side. In this case:
// https://www.googleapis.com/auth/calendar
// You can check granted_scopes from within:
// context.additionalUserInfo.profile.granted_scopes (space joined list of scopes).
// Set access token/refresh token.
oAuth2Client.setCredentials({
access_token: context.credential.accessToken,
refresh_token: context.credential.refreshToken,
});
const calendar = google.calendar('v3');
// Setup Onboarding event on user's calendar.
const event = {/** ... */};
calendar.events.insert({
auth: oauth2client,
calendarId: 'primary',
resource: event,
}, (err, event) => {
// Do not fail. This is a best effort approach.
resolve();
});
});
})
}
});