54

Desidero che gli utenti siano in grado di autenticarsi con la mia applicazione Firebase utilizzando più provider di autenticazione diversi, ad esempio Facebook, Twitter o Github. Una volta autenticato, voglio che gli utenti abbiano accesso allo stesso account indipendentemente dal metodo di autenticazione utilizzato.Come posso accedere con più servizi sociali con Firebase?

In altre parole, voglio unire più metodi di autenticazione in un singolo account all'interno della mia app. Come posso farlo in un'app Firebase?

risposta

55

Aggiornamento (20.160.521): Firebase appena rilasciato un importante aggiornamento per il suo Firebase Authentication prodotto, che ora consente a un singolo utente di collegare gli account dei vari provider supportati. Per ulteriori informazioni su questa funzione, leggere la documentazione per iOS, Web e Android. La risposta sotto è lasciata per ragioni storiche.


Il nucleo servizio Firebase fornisce diversi metodi per l'autenticazione: https://www.firebase.com/docs/security/authentication.html

Al suo interno, Firebase utilizza token JWT sicure per l'autenticazione. Tutto ciò che risulta nella produzione di un token JWT (come l'utilizzo di una libreria JWT sul proprio server) funzionerà per autenticare gli utenti su Firebase, in modo da avere il controllo completo sul processo di autenticazione.

Firebase fornisce un servizio chiamato Firebase Simple Login che è un modo per generare questi token (questo fornisce l'autenticazione Facebook, Twitter, ecc.). È concepito per scenari di autenticazione comuni in modo che tu possa essere subito operativo senza server, ma non è l'unico modo per autenticarsi e non è inteso per essere una soluzione completa.

Ecco un approccio per consentire login con diversi operatori di utilizzare Firebase semplice accesso:

  1. memorizzare un identificativo utente canonica per ogni utente, e una mappatura per ogni identificatore specifico del provider a quello id canonica.
  2. Aggiorna le tue regole di sicurezza per abbinare le credenziali su un account utente dato, invece di uno solo.

In pratica, le norme di sicurezza potrebbero apparire così, supponendo che si desidera abilitare sia l'autenticazione di Twitter e Facebook (o consentire a un utente di creare un account con uno e poi aggiungere gli altri):

{ 
    "users": { 
    "$userid": { 
     // Require the user to be logged in, and make sure their current credentials 
     // match at least one of the credentials listed below, unless we're creating 
     // a new account from scratch. 
     ".write": "auth != null && 
     (data.val() === null || 
     (auth.provider === 'facebook' && auth.id === data.child('facebook/id').val() || 
     (auth.provider === 'twitter' && auth.id === data.child('twitter/id').val()))" 
    } 
    }, 
    "user-mappings": { 
    // Only allow users to read the user id mapping for their own account. 
    "facebook": { 
     "$fbuid": { 
     ".read": "auth != null && auth.provider === 'facebook' && auth.id === $fbuid", 
     ".write": "auth != null && 
      (data.val() == null || 
      root.child('users').child(data.val()).child('facebook-id').val() == auth.id)" 
     } 
    }, 
    "twitter": { 
     "$twuid": { 
     ".read": "auth != null && auth.provider === 'twitter' && auth.id === $twuid", 
     ".write": "auth != null && 
      (data.val() == null || 
      root.child('users').child(data.val()).child('twitter-id').val() == auth.id)" 
     } 
    } 
    } 
} 

In questo esempio, si memorizza un ID utente globale (che può essere qualsiasi cosa si scelga) e si mantiene la mappatura tra i meccanismi di autenticazione di Facebook, Twitter, ecc. Al record dell'utente principale. Al momento dell'accesso per ciascun utente, recupererai il record utente principale dai mapping utente e utilizzerai quell'ID come archivio principale di dati e azioni dell'utente. Quanto sopra limita e convalida i dati nelle mappature degli utenti in modo che possano essere scritti solo dall'utente corretto che ha già lo stesso ID utente di Facebook, Twitter, ecc. Sotto/utenti/$ userid/(id-facebook | twitter -id | etc-id).

Questo metodo consente di essere subito operativi. Tuttavia, se si dispone di un caso d'uso complicato e si desidera ottenere il controllo completo dell'esperienza di autenticazione, è possibile eseguire il proprio codice di autenticazione sui propri server.Esistono molte utili librerie open source che è possibile utilizzare per eseguire questa operazione, ad esempio everyauth e passport.

È inoltre possibile eseguire l'autenticazione utilizzando provider di autenticazione di terze parti. Ad esempio, è possibile utilizzare Singly, che ha un'enorme varietà di integrazioni out-of-the-box senza dover scrivere alcun codice lato server.

+1

ho alcune domande per voi su questa strategia trovato qui: http://stackoverflow.com/questions/22886053/can-the-firebase-auth-object -handle-simultanea-authentication-types –

+0

Non riesco a vedere la differenza tra '.child ('twitter/id'). val()' e '.child ('twitter-id'). val()'. Sono entrambi riferimenti diversi? Non l'ho capito. – Jobsamuel

+0

Grazie mille per l'esempio di mappatura. Sarebbe possibile seguire un po 'di codice su come farlo su iOS? –

16

So che questo post esiste da mesi ma quando ho affrontato questo problema, mi ci è voluto molto tempo per rendere il codice più flessibile. Base su codice Andrew sopra, ho ottimizzato il codice un po '. negozio

campione di dati:

userMappings 
|---facebook:777 
| |---user:"123" 
|---twitter:888 
    |---user:"123" 
users 
|---123 
    |---userMappings 
     |---facebook: "facebook:777" 
     |---twitter: "twitter:888" 

regole di sicurezza:

"userMappings": { 
    "$login_id": { 
    ".read": "$login_id === auth.uid", 
    ".write": "auth!== null && (data.val() === null || $login_id === auth.uid)" 
    } 
}, 

"users": { 
    "$user_id": { 
    ".read": "data.child('userMappings/'+auth.provider).val()===auth.uid", 
    ".write": "auth!= null && (data.val() === null || data.child('userMappings/'+auth.provider).val()===auth.uid)" 
    } 
} 

Così userMappings è ancora la prima informazioni guardiamo in su quando effettuare il login da Facebook, Twitter ... . L'utente dell'utenteuserMappings punta all'account principale negli utenti . Quindi, dopo aver effettuato il login da Facebook o Twitter, possiamo cercare l'account utente principale. Negli utenti manteniamo l'elenco di userMapping che può accedere ai suoi dati.

Quando creiamo un nuovo utente, dobbiamo prima creare un account negli utenti . L'id per l'utente negli utenti potrebbe essere qualsiasi cosa desideriamo. Questo è flessibile perché possiamo fornire più metodi di accesso come Google, Github senza aggiungere più regole di sicurezza.

+1

Questo mi sembra una grande soluzione, ma mi lascia chiedevo/bloccato per un aspetto: le richieste con l'auth.uid corretta saranno in grado di accedere al "userMapping" di cui ha bisogno, naturalmente (controllare!), E sarà anche in grado di accedere all '"utente" di cui ha bisogno tramite una ricerca sul suo figlio "userMappings" (leggermente goffo, ma controlla!). Ma che dire quando abbiamo altri dati/campi, diciamo "userActivity" o "userPosts", come si renderebbero quei campi scrivibili solo dall'utente che li ha creati? Sembra che dovrebbero anche avere un figlio "userMappings" con tutte le chiavi di accesso per quell'utente .. – tydbwjiiofj23iof2jiojf2ifj3i2

+0

@MathewHuuskoV: Penso che in "userActivity" o "userPosts" ..., possiamo controllare la regola di sicurezza utilizzando la variabile predeterminata "root" per cercare l'ID utente in userMappings. Per esempio. ".write": "root.child ('userMappings /' + auth.uid) .child ('utente'). val() === data.child ('autore'). val()". Quale 'autore' contiene l'ID utente dell'autore del post. – Kuma

+1

Qualcuno ha già implementato qualcosa di simile? Esistono esempi di codice per questo tipo di soluzione? Desidero aggiungere l'accesso Firebase - allo stesso account - utilizzando sia l'email/password o Facebook alla mia app iOS. Vedo un paio di scenari che rendono l'intera cosa piuttosto complicata, a seconda del login dell'utente e se l'utente ha precedentemente creato l'account tramite e-mail o Facebook. Ad esempio, se l'utente effettua l'accesso utilizzando Facebook, ma in precedenza ha utilizzato solo e-mail/password per accedere, come dovrei trovare l'utente corrispondente nel mio database Firebase? O vice versa. – andlin

0

Ho passato un po 'di tempo a pensare a una buona soluzione, e IMHO per essere in grado di registrare da qualsiasi fornitore è solo confusione. Nel mio front end chiedo sempre una registrazione via email, quindi un utente che accede con facebook e google +, ad esempio, verrebbe registrato come lo stesso utente quando informa la sua email.

In questo modo, i dati di esempio proposti da Kuma non devono duplicare le UserMappings.

Sample Data Store:

userMappings 
|---facebook:777 
| |---user:"123" 
|---twitter:888 
    |---user:"123" 
users 
|---123 
    |---user data