Le funzioni di blocco ti consentono di eseguire codice personalizzato che modifica il risultato utente che si registra o accede alla tua app. Ad esempio, puoi impedire a un utente dall'autenticazione se non soddisfano determinati criteri o aggiornano le informazioni prima di restituirle all'app client.
Prima di iniziare
Per utilizzare le funzioni di blocco, devi eseguire l'upgrade del progetto Firebase a Firebase Authentication with Identity Platform. Se non hai ancora eseguito l'upgrade, fallo prima.
Informazioni sulle funzioni di blocco
Puoi registrare funzioni di blocco per due eventi:
beforeCreate
: si attiva prima che un nuovo utente venga salvato nella Firebase Authentication e prima che un token venga restituito al tuo dell'app client.beforeSignIn
: si attiva dopo la verifica delle credenziali di un utente, ma prima che Firebase Authentication restituisca un token ID all'app client. Se la tua app utilizza l'autenticazione a più fattori, la funzione si attiva dopo che l'utente ha verificato il secondo fattore. Tieni presente che la creazione di una nuova l'utente attiva anchebeforeSignIn
, oltre abeforeCreate
.
Quando utilizzi le funzioni di blocco, tieni presente quanto segue:
La tua funzione deve rispondere entro 7 secondi. Dopo 7 secondi, Firebase Authentication restituisce un errore e l'operazione del client non riesce.
Alle app client vengono trasmessi codici di risposta HTTP diversi da
200
. Ensure il codice client gestisce gli errori che la funzione può restituire.Le funzioni si applicano a tutti gli utenti del progetto, inclusi quelli contenuti in un tenant. Firebase Authentication fornisce informazioni sugli utenti per la funzione, tra cui: i tenant a cui appartengono, in modo che tu possa rispondere di conseguenza.
Il collegamento di un altro provider di identità a un account riattiva tutte le funzioni
beforeSignIn
registrate.L'autenticazione anonima e personalizzata non attiva le funzioni di blocco.
Esegui il deployment di una funzione di blocco
Per inserire il codice personalizzato nei flussi di autenticazione utente, implementa le funzioni di blocco. Una volta eseguito il deployment delle funzioni di blocco, il codice personalizzato completata correttamente per consentire l'autenticazione e la creazione degli utenti.
Esegui il deployment di una funzione di blocco nello stesso modo in cui esegui il deployment di qualsiasi funzione. (per maggiori dettagli, consulta la pagina Cloud Functions Guida introduttiva). In sintesi:
Scrivi Cloud Functions che gestisce l'evento
beforeCreate
,beforeSignIn
evento o entrambi.Ad esempio, per iniziare, puoi aggiungere le seguenti funzioni no-op a
index.js
:const functions = require('firebase-functions/v1'); exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => { // TODO }); exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => { // TODO });
Negli esempi precedenti è stata omessa l'implementazione della logica di autenticazione personalizzata. Consulta le sezioni seguenti per imparare a implementare le funzioni di blocco e Scenari comuni per esempi specifici.
Esegui il deployment delle funzioni utilizzando l'interfaccia a riga di comando Firebase:
firebase deploy --only functions
Devi eseguire nuovamente il deployment delle funzioni ogni volta che le aggiorni.
Ottenere informazioni sugli utenti e sul contesto
Gli eventi beforeSignIn
e beforeCreate
forniscono oggetti User
e EventContext
che contengono informazioni sull'accesso dell'utente. Utilizza questi valori
nel codice per determinare se consentire o meno il proseguimento di un'operazione.
Per un elenco delle proprietà disponibili nell'oggetto User
, consulta il
riferimento all'API UserRecord
.
L'oggetto EventContext
contiene le seguenti proprietà:
Nome | Descrizione | Esempio |
---|---|---|
locale |
La lingua dell'applicazione. Puoi impostare le impostazioni internazionali utilizzando SDK client o passando l'intestazione delle impostazioni internazionali nell'API REST. | fr o sv-SE |
ipAddress
| L'indirizzo IP del dispositivo che l'utente finale sta registrando o accedendo da cui proviene. | 114.14.200.1 |
userAgent
| Lo user agent che attiva la funzione di blocco. | Mozilla/5.0 (X11; Linux x86_64) |
eventId
| L'identificatore univoco dell'evento. | rWsyPtolplG2TBFoOkkgyg |
eventType
|
Il tipo di evento. Fornisce informazioni sul nome dell'evento, ad esempio
beforeSignIn o beforeCreate , e sul
metodo di accesso associato utilizzato, ad esempio Google o email/password.
|
providers/cloud.auth/eventTypes/user.beforeSignIn:password
|
authType
| Sempre USER . |
USER
|
resource
| Il progetto o il tenant Firebase Authentication. |
projects/project-id/tenants/tenant-id
|
timestamp
| L'ora in cui l'evento è stato attivato, formattato come Stringa RFC 3339. | Tue, 23 Jul 2019 21:10:57 GMT
|
additionalUserInfo
| Un oggetto che contiene informazioni sull'utente. |
AdditionalUserInfo
|
credential
| Un oggetto contenente informazioni sulla credenziale dell'utente. |
AuthCredential
|
Blocco della registrazione o dell'accesso
Per bloccare un tentativo di registrazione o di accesso, inserisci un HttpsError
nel tuo
personalizzata. Ad esempio:
Node.js
throw new functions.auth.HttpsError('permission-denied');
La tabella seguente elenca gli errori che puoi segnalare e i relativi valori predefiniti messaggio di errore:
Nome | Codice | Messaggio |
---|---|---|
invalid-argument |
400 |
Il client ha specificato un argomento non valido. |
failed-precondition |
400 |
La richiesta non può essere eseguita nello stato attuale del sistema. |
out-of-range |
400 |
Il client ha specificato un intervallo non valido. |
unauthenticated |
401 |
Token OAuth mancante, non valido o scaduto. |
permission-denied |
403 |
Il client non dispone di autorizzazioni sufficienti. |
not-found |
404 |
La risorsa specificata non è stata trovata. |
aborted |
409 |
Conflitto di contemporaneità, ad esempio un conflitto di lettura, modifica e scrittura. |
already-exists |
409 |
La risorsa che un client ha cercato di creare esiste già. |
resource-exhausted |
429 |
Quota di risorse esaurita o vicina alla limitazione della frequenza. |
cancelled |
499 |
La richiesta è stata annullata dal client. |
data-loss |
500 |
Perdita di dati non recuperabili o danneggiamento dei dati. |
unknown |
500 |
Errore sconosciuto del server. |
internal |
500 |
Errore interno del server. |
not-implemented |
501 |
Metodo API non implementato dal server. |
unavailable |
503 |
Servizio non disponibile. |
deadline-exceeded |
504 |
Scadenza richiesta superata. |
Puoi anche specificare un messaggio di errore personalizzato:
Node.js
throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');
L'esempio seguente mostra come bloccare gli utenti che non si trovano all'interno di uno specifico dominio dalla registrazione per la tua app:
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}"`);
}
});
Che tu stia utilizzando un messaggio predefinito o personalizzato, Cloud Functions aggrega l'errore e lo restituisce al client come errore interno. Ad esempio:
throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);
L'app dovrebbe rilevare l'errore e gestirlo di conseguenza. Ad esempio:
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.
}
});
Modifica di un utente
Invece di bloccare un tentativo di registrazione o accesso, puoi consentire la prosecuzione dell'operazione, ma modificare l'oggetto User
salvato nel database di Firebase Authentication e restituito al client.
Per modificare un utente, restituisci un oggetto dal tuo gestore di eventi contenente il metodo campi da modificare. Puoi modificare i seguenti campi:
displayName
disabled
emailVerified
photoUrl
customClaims
sessionClaims
(solobeforeSignIn
)
Ad eccezione di sessionClaims
, tutti i campi modificati vengono salvati in
database di Firebase Authentication, il che significa che è incluso nella risposta
e vengono conservati tra una sessione utente e l'altra.
L'esempio seguente mostra come impostare un nome visualizzato predefinito:
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';
};
});
Se registri un gestore di eventi sia per beforeCreate
sia per beforeSignIn
,
nota che beforeSignIn
viene eseguito dopo beforeCreate
. Campi utente aggiornati in
beforeCreate
sono visibili in beforeSignIn
. Se imposti un campo diverso da
sessionClaims
in entrambi i gestori eventi, il valore impostato in beforeSignIn
sovrascrive il valore impostato in beforeCreate
. Solo per sessionClaims
, sono
propagati alle attestazioni dei token della sessione corrente, ma non vengono resi persistenti o
archiviati nel database.
Ad esempio, se sono impostati valori sessionClaims
, beforeSignIn
li restituirà insieme a eventuali rivendicazioni beforeCreate
e verranno uniti. Quando vengono uniti, se
una chiave sessionClaims
corrisponde a una chiave in customClaims
, la chiave
Il campo customClaims
verrà sovrascritto nelle rivendicazioni dei token da sessionClaims
chiave. Tuttavia, la chiave customClaims
sovrascritta continuerà a essere presente nel database per le richieste future.
Credenziali e dati OAuth supportati
Puoi passare le credenziali OAuth e i dati alle funzioni di blocco di vari fornitori di servizi di identità. La tabella seguente mostra quali sono le credenziali e i dati supportate per ogni provider di identità:
Provider di identità | Token ID | Token di accesso | Data di scadenza | Secret token | Token di aggiornamento | Richieste di accesso |
---|---|---|---|---|---|---|
Sì | Sì | Sì | No | Sì | No | |
No | Sì | Sì | No | No | No | |
No | Sì | No | Sì | No | No | |
GitHub | No | Sì | No | No | No | No |
Microsoft | Sì | Sì | Sì | No | Sì | No |
No | Sì | Sì | No | No | No | |
Yahoo | Sì | Sì | Sì | No | Sì | No |
Apple | Sì | Sì | Sì | No | Sì | No |
SAML | No | No | No | No | No | Sì |
OIDC | Sì | Sì | Sì | No | Sì | Sì |
Aggiorna token
Per utilizzare un token di aggiornamento in una funzione di blocco, devi prima selezionare il token nella pagina Funzioni di blocco della console Firebase.
I token di aggiornamento non verranno restituiti da alcun provider di identità al momento dell'accesso. direttamente con una credenziale OAuth, ad esempio un token ID o un token di accesso. In questa situazione, la stessa credenziale OAuth lato client verrà passata alla funzione di blocco.
Le sezioni seguenti descrivono tutti i tipi di provider di identità e i tipi di provider supportati credenziali e dati.
Provider OIDC generici
Quando un utente accede con un provider OIDC generico, vengono utilizzate le seguenti credenziali verranno passate:
- Token ID: fornito se è selezionato il flusso
id_token
. - Token di accesso: fornito se è selezionato il flusso del codice. Tieni presente che il codice è attualmente supportato solo tramite l'API REST.
- Token di aggiornamento. Viene fornito se
offline_access
ambito è selezionata.
Esempio:
const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
Quando un utente accede con Google, vengono passate le seguenti credenziali:
- Token ID
- Token di accesso
- Token di aggiornamento: fornito solo se i seguenti parametri personalizzati sono
richiesto:
access_type=offline
prompt=consent
, se l'utente aveva già espresso il suo consenso, ma nessun è stato richiesto un nuovo ambito
Esempio:
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
'access_type': 'offline',
'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);
Scopri di più sui token di aggiornamento di Google.
Quando un utente accede con Facebook, verrà passata la seguente credenziale:
- Token di accesso: viene restituito un token di accesso che può essere scambiato con con un altro token di accesso. Scopri di più sui diversi tipi di token di accesso supportate da Facebook e su come scambiarle token di lunga durata.
GitHub
Quando un utente accede con GitHub, verrà passata la seguente credenziale:
- Token di accesso: non scade a meno che non venga revocato.
Microsoft
Quando un utente accede con Microsoft, verranno passate le seguenti credenziali:
- Token ID
- Token di accesso
- Token di aggiornamento. Viene passato alla funzione di blocco se il valore
offline_access
ambito è selezionata.
Esempio:
const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
Yahoo
Quando un utente accede con Yahoo, vengono trasmesse le seguenti credenziali senza parametri o ambiti personalizzati:
- Token ID
- Token di accesso
- Token di aggiornamento
Quando un utente accede con LinkedIn, verrà passata la seguente credenziale:
- Token di accesso
Apple
Quando un utente accede con Apple, verranno passate le seguenti credenziali senza parametri o ambiti personalizzati:
- Token ID
- Token di accesso
- Aggiorna token
Scenari comuni
I seguenti esempi mostrano alcuni casi d'uso comuni per le funzioni di blocco:
Consentire solo la registrazione da un dominio specifico
L'esempio seguente mostra come impedire agli utenti che non fanno parte del
example.com
dominio dalla registrazione con la tua app:
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}"`);
}
});
Bloccare la registrazione degli utenti con indirizzi email non verificati
L'esempio seguente mostra come impedire agli utenti con indirizzi email non verificati di registrazione con la tua app:
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}"`);
}
});
Richiedere la verifica email al momento della registrazione
L'esempio seguente mostra come richiedere a un utente di verificare la propria email dopo registrazione:
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.`);
}
});
Trattare determinate email del provider di identità come verificate
L'esempio seguente mostra come trattare le email degli utenti provenienti da una determinata identità Fornitori come verificati:
Node.js
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
return {
emailVerified: true,
};
}
});
Bloccare l'accesso da determinati indirizzi IP
L'esempio seguente mostra come bloccare l'accesso da determinati intervalli di indirizzi IP:
Node.js
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
if (isSuspiciousIpAddress(context.ipAddress)) {
throw new functions.auth.HttpsError(
'permission-denied', 'Unauthorized access!');
}
});
Impostazione di attestazioni personalizzate e di sessione
L'esempio seguente mostra come impostare rivendicazioni personalizzate e di sessione:
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,
}
}
}
});
Monitoraggio degli indirizzi IP per monitorare le attività sospette
Puoi impedire il furto di token monitorando l'indirizzo IP da cui un utente accede e confrontandolo con l'indirizzo IP nelle richieste successive. Se la richiesta sembrano sospetti; ad esempio, gli IP provengono da località geografiche diverse regioni — puoi chiedere all'utente di accedere di nuovo.
Utilizza le attestazioni di sessione per monitorare l'indirizzo IP con cui l'utente accede:
Node.js
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => { return { sessionClaims: { signInIpAddress: context.ipAddress, }, }; });
Quando un utente tenta di accedere a risorse che richiedono l'autenticazione con Firebase Authentication, confronta l'indirizzo IP nella richiesta con l'IP utilizzato per accedere:
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!' }) }); } }); });
Controllo delle foto degli utenti
L'esempio seguente mostra come sanificare gli utenti foto del profilo:
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,
};
}
});
});
Per scoprire di più su come rilevare e sanificare le immagini, consulta la documentazione di Cloud Vision.
Accesso alle credenziali OAuth del provider di identità di un utente
L'esempio seguente mostra come ottenere un token di aggiornamento per un utente che hanno eseguito l'accesso con Google e lo utilizzano per chiamare le API di Google Calendar. La viene memorizzato per l'accesso 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();
});
});
})
}
});
Esegui l'override dell'esito di reCAPTCHA Enterprise per le operazioni utente
L'esempio seguente mostra come eseguire l'override di un esito reCAPTCHA Enterprise per i flussi utente supportati.
Consulta la sezione Abilitare reCAPTCHA Enterprise per scoprire di più sull'integrazione di reCAPTCHA Enterprise con Firebase Authentication.
Le funzioni di blocco possono essere utilizzate per consentire o bloccare i flussi in base a fattori personalizzati, sostituendo così il risultato fornito da reCAPTCHA Enterprise.
Node.js
const {
auth,
} = require("firebase-functions/v1");
exports.checkrecaptchaV1 = auth.user().beforeSignIn((userRecord, context) => {
// Allow users with a specific email domain to sign in regardless of their recaptcha score.
if (userRecord.email && userRecord.email.indexOf('@acme.com') === -1) {
return {
recaptchaActionOverride: 'ALLOW',
};
}
// Allow users to sign in with recaptcha score greater than 0.5
if (context.additionalUserInfo.recaptchaScore > 0.5) {
return {
recaptchaActionOverride: 'ALLOW',
};
}
// Block all others.
return {
recaptchaActionOverride: 'BLOCK',
};
});