Autenticazione tramite Apple su Android

Puoi consentire agli utenti di eseguire l'autenticazione con Firebase utilizzando il loro ID Apple: utilizzando l'SDK Firebase per eseguire il flusso di accesso end-to-end OAuth 2.0.

Prima di iniziare

Per consentire agli utenti di accedere utilizzando Apple, configura prima Accedi con Apple sul sito per sviluppatori di Apple, quindi attiva Apple come provider di accesso per il tuo progetto Firebase.

Partecipa all'Apple Developer Program

Accedi con Apple può essere configurato solo dai membri del programma Apple Developer.

Configura Accedi con Apple

Sul sito Apple Developer:

  1. Associa il tuo sito web alla tua app come descritto nella prima sezione di Configurare Accedi con Apple per il web. Quando richiesto, registra il seguente URL come URL di ritorno:

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    Puoi inserire l'ID progetto Firebase nella Console Firebase pagina delle impostazioni.

    Al termine, prendi nota del tuo nuovo ID servizio, che dovrai inserire nella prossima sezione.

  2. Crea un Accedi con la chiave privata Apple. Nella sezione successiva avrai bisogno della nuova chiave privata e del nuovo ID chiave.
  3. Se utilizzi una qualsiasi delle funzionalità di Firebase Authentication che inviano email agli utenti, inclusi accesso tramite link email, verifica indirizzo email, modifica account revoca e altri, configurare il servizio di inoltro email privato di Apple e registrare noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (o il tuo dominio personalizzato per il modello di email) in modo che Apple possa inoltrare le email inviate da Firebase Authentication agli indirizzi email Apple anonimizzati.

Abilita Apple come provider di accesso

  1. Aggiungi Firebase al tuo progetto Android. Assicurati di registrare la firma SHA-1 dell'app quando la configuri nella console Firebase.
  2. Nella console Firebase, apri la sezione Autorizzazione. Nella scheda Metodo di accesso, Attiva il provider Apple. Specifica l'ID servizio che hai creato nella sezione precedente. Inoltre, nel Sezione di configurazione del flusso di codice OAuth, specifica il tuo ID team Apple e la chiave privata e l'ID chiave creati nella sezione precedente.

Rispettare i requisiti dei dati anonimizzati di Apple

Accedi con Apple offre agli utenti la possibilità di anonimizzare i propri dati, incluso l'indirizzo email, al momento dell'accesso. Gli utenti che scelgono questa opzione hanno indirizzi email con il dominio privaterelay.appleid.com. Quando utilizzi Accedi con Apple nella tua app, devi rispettare eventuali norme o termini per gli sviluppatori di Apple applicabili in merito a questi ID Apple anonimizzati.

Ciò include l'ottenimento del consenso dell'utente richiesto prima di associare informazioni personali che consentono l'identificazione diretta a un ID Apple anonimizzato. Quando utilizzi Firebase Authentication, potrebbero essere incluse le seguenti azioni:

  • Collega un indirizzo email a un ID Apple anonimizzato o viceversa.
  • Collega un numero di telefono a un ID Apple anonimo o viceversa
  • Collegare una credenziale social non anonima (Facebook, Google e così via) a un ID Apple anonimizzato o viceversa.

L'elenco riportato sopra non è esaustivo. Fai riferimento all'Apple Developer Program il Contratto di licenza nella sezione Abbonamento del tuo account sviluppatore per assicurati che la tua app soddisfi i requisiti di Apple.

Gestire il flusso di accesso con l'SDK Firebase

Su Android, il modo più semplice per autenticare i tuoi utenti con Firebase utilizzando il loro Gli account Apple devono gestire l'intero flusso di accesso con Firebase per Android l'SDK.

Per gestire il flusso di accesso con l'SDK Firebase per Android:

  1. Costruisci un'istanza di un OAuthProvider utilizzando il relativo builder con l'ID fornitore apple.com:

    Kotlin+KTX

    val provider = OAuthProvider.newBuilder("apple.com")
    

    Java

    OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
    
  2. Facoltativo: specifica gli ambiti OAuth 2.0 aggiuntivi rispetto a quelli predefiniti che vuoi richiedere al provider di autenticazione.

    Kotlin+KTX

    provider.setScopes(arrayOf("email", "name"))
    

    Java

    List<String> scopes =
        new ArrayList<String>() {
          {
            add("email");
            add("name");
          }
        };
    provider.setScopes(scopes);
    

    Per impostazione predefinita, quando l'opzione Un account per indirizzo email è attivata, Firebase richiede gli ambiti email e nome. Se cambi questa impostazione in Più di account per indirizzo email, Firebase non richiede alcun ambito a Apple se non li specifichi.

  3. Facoltativo: se vuoi visualizzare la schermata di accesso di Apple in una lingua diversa dall'inglese, imposta il parametro locale. Consulta le Accedi con la documentazione di Apple per i paesi supportati.

    Kotlin+KTX

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr")
    

    Java

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr");
    
  4. Esegui l'autenticazione con Firebase utilizzando l'oggetto provider OAuth. Tieni presente che, a differenza di altre operazioni FirebaseAuth, questa acquisirà il controllo dell'interfaccia utente aprendo una scheda personalizzata di Chrome. Di conseguenza, non fare riferimento all'attività in OnSuccessListener e OnFailureListener che colleghi, poiché si disattaccheranno immediatamente quando l'operazione avvia l'interfaccia utente.

    Per prima cosa, controlla se hai già ricevuto una risposta. Se accedi con questo metodo, la tua attività viene messa in background, il che significa che può essere recuperata dal sistema durante il flusso di accesso. Per rendere assicurarti di non far riprovare l'utente in questo caso, devi per controllare se un risultato è già presente.

    Per verificare se è presente un risultato in sospeso, chiama getPendingAuthResult():

    Kotlin+KTX

    val pending = auth.pendingAuthResult
    if (pending != null) {
        pending.addOnSuccessListener { authResult ->
            Log.d(TAG, "checkPending:onSuccess:$authResult")
            // Get the user profile with authResult.getUser() and
            // authResult.getAdditionalUserInfo(), and the ID
            // token from Apple with authResult.getCredential().
        }.addOnFailureListener { e ->
            Log.w(TAG, "checkPending:onFailure", e)
        }
    } else {
        Log.d(TAG, "pending: null")
    }
    

    Java

    mAuth = FirebaseAuth.getInstance();
    Task<AuthResult> pending = mAuth.getPendingAuthResult();
    if (pending != null) {
        pending.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                Log.d(TAG, "checkPending:onSuccess:" + authResult);
                // Get the user profile with authResult.getUser() and
                // authResult.getAdditionalUserInfo(), and the ID
                // token from Apple with authResult.getCredential().
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "checkPending:onFailure", e);
            }
        });
    } else {
        Log.d(TAG, "pending: null");
    }
    

    Se non ci sono risultati in sospeso, avvia il flusso di accesso chiamando startActivityForSignInWithProvider():

    Kotlin+KTX

    auth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener { authResult ->
                // Sign-in successful!
                Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}")
                val user = authResult.user
                // ...
            }
            .addOnFailureListener { e ->
                Log.w(TAG, "activitySignIn:onFailure", e)
            }
    

    Java

    mAuth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener(
                    new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            // Sign-in successful!
                            Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser());
                            FirebaseUser user = authResult.getUser();
                            // ...
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.w(TAG, "activitySignIn:onFailure", e);
                        }
                    });
    

    A differenza di altri provider supportati da Firebase Auth, Apple non fornisce una l'URL della foto.

    Inoltre, quando l'utente sceglie di non condividere il proprio indirizzo email con l'app, Apple fornisce un indirizzo email univoco per l'utente (del tipo xyz@privaterelay.appleid.com), che condivide con la tua app. Se hai configurato il servizio di inoltro email privato, Apple inoltra le email inviate all'indirizzo anonimizzato all'indirizzo email reale dell'utente.

    Apple condivide le informazioni dell'utente, come il nome visualizzato, con le app solo la prima volta che l'utente accede. In genere, Firebase memorizza il nome visualizzato la prima volta che un utente accede con Apple, che puoi ottenere con getCurrentUser().getDisplayName(). Tuttavia, se in precedenza hai utilizzato Apple per far accedere un utente all'app senza utilizzare Firebase, Apple non fornirà a Firebase il nome visualizzato dell'utente.

Riconvalida e collegamento dell'account

È possibile usare lo stesso pattern con startActivityForReauthenticateWithProvider() che puoi utilizzare per recuperare una nuova credenziale per le operazioni sensibili che richiedono un accesso recente:

Kotlin+KTX

// The user is already signed-in.
val firebaseUser = auth.getCurrentUser()

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener( authResult -> {
        // User is re-authenticated with fresh tokens and
        // should be able to perform sensitive operations
        // like account deletion and email or password
        // update.
    })
    .addOnFailureListener( e -> {
        // Handle failure.
    })

Java

// The user is already signed-in.
FirebaseUser firebaseUser = mAuth.getCurrentUser();

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener(
        new OnSuccessListener<AuthResult>() {
          @Override
          public void onSuccess(AuthResult authResult) {
            // User is re-authenticated with fresh tokens and
            // should be able to perform sensitive operations
            // like account deletion and email or password
            // update.
          }
        })
    .addOnFailureListener(
        new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Handle failure.
          }
        });

Inoltre, puoi utilizzare linkWithCredential() per collegare diversi provider di identità agli account esistenti.

Tieni presente che Apple richiede di ottenere il consenso esplicito degli utenti prima del collegamento i propri account Apple ad altri dati.

Ad esempio, per collegare un account Facebook all'account Firebase corrente, utilizza la token di accesso ottenuto per l'accesso dell'utente a Facebook:

Kotlin+KTX

// Initialize a Facebook credential with a Facebook access token.
val credential = FacebookAuthProvider.getCredential(token.getToken())

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, task -> {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      });

Java

// Initialize a Facebook credential with a Facebook access token.
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
      @Override
      public void onComplete(@NonNull Task<AuthResult> task) {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      }
    });

Impostazioni avanzate: gestire manualmente il flusso di accesso

Puoi anche eseguire l'autenticazione con Firebase utilizzando un account Apple gestendo il flusso di accesso utilizzando l'SDK JS di accesso Apple, creando manualmente il flusso OAuth o utilizzando una libreria OAuth come AppAuth.

  1. Per ogni richiesta di accesso, genera una stringa casuale, un "nonce", che utilizzerai per assicurarti che il token ID che ricevi sia stato concesso specificamente in risposta alla richiesta di autenticazione della tua app. Questo passaggio è importante per prevenire gli attacchi di replay.

    Puoi generare un nonce crittograficamente sicuro su Android con SecureRandom, come nell'esempio seguente:

    Kotlin+KTX

    private fun generateNonce(length: Int): String {
        val generator = SecureRandom()
    
        val charsetDecoder = StandardCharsets.US_ASCII.newDecoder()
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE)
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE)
    
        val bytes = ByteArray(length)
        val inBuffer = ByteBuffer.wrap(bytes)
        val outBuffer = CharBuffer.allocate(length)
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes)
            inBuffer.rewind()
            charsetDecoder.reset()
            charsetDecoder.decode(inBuffer, outBuffer, false)
        }
        outBuffer.flip()
        return outBuffer.toString()
    }
    

    Java

    private String generateNonce(int length) {
        SecureRandom generator = new SecureRandom();
    
        CharsetDecoder charsetDecoder = StandardCharsets.US_ASCII.newDecoder();
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE);
    
        byte[] bytes = new byte[length];
        ByteBuffer inBuffer = ByteBuffer.wrap(bytes);
        CharBuffer outBuffer = CharBuffer.allocate(length);
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes);
            inBuffer.rewind();
            charsetDecoder.reset();
            charsetDecoder.decode(inBuffer, outBuffer, false);
        }
        outBuffer.flip();
        return outBuffer.toString();
    }
    

    Quindi, ottieni l'hash SHA246 del nonce come stringa esadecimale:

    Kotlin+KTX

    private fun sha256(s: String): String {
        val md = MessageDigest.getInstance("SHA-256")
        val digest = md.digest(s.toByteArray())
        val hash = StringBuilder()
        for (c in digest) {
            hash.append(String.format("%02x", c))
        }
        return hash.toString()
    }
    

    Java

    private String sha256(String s) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest(s.getBytes());
        StringBuilder hash = new StringBuilder();
        for (byte c: digest) {
            hash.append(String.format("%02x", c));
        }
        return hash.toString();
    }
    

    Invierai l'hash SHA256 del nonce con la richiesta di accesso, che Apple trasmetterà invariato nella risposta. Firebase convalida la risposta eseguendo l'hashing del nonce originale e confrontandolo con il valore trasmesso da Apple.

  2. Avvia il flusso di accesso di Apple utilizzando la tua libreria OAuth o un altro metodo. Essere assicurati di includere il nonce con hash nella richiesta.

  3. Dopo aver ricevuto la risposta di Apple, recupera il token ID dalla risposta e utilizzalo e il nonce non sottoposto ad hashing per creare un AuthCredential:

    Kotlin+KTX

    val credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build()
    

    Java

    AuthCredential credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build();
    
  4. Esegui l'autenticazione con Firebase utilizzando la credenziale Firebase:

    Kotlin+KTX

    auth.signInWithCredential(credential)
          .addOnCompleteListener(this) { task ->
              if (task.isSuccessful) {
                // User successfully signed in with Apple ID token.
                // ...
              }
          }
    

    Java

    mAuth.signInWithCredential(credential)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
          @Override
          public void onComplete(@NonNull Task<AuthResult> task) {
            if (task.isSuccessful()) {
              // User successfully signed in with Apple ID token.
              // ...
            }
          }
        });
    

Se la chiamata a signInWithCredential ha esito positivo, puoi utilizzare getCurrentUser per ottenere i dati dell'account dell'utente.

Revoca token

Apple richiede che le app che supportano la creazione di account debbano consentire agli utenti di avviare l'eliminazione dell'account nell'app, come descritto nella sezione Controllo dello store Linee guida

Inoltre, le app che supportano Accedi con Apple devono utilizzare l'API REST Accedi con Apple per revocare i token utente.

Per soddisfare questo requisito, implementa i seguenti passaggi:

  1. Utilizza il metodo startActivityForSignInWithProvider() per accedere con Apple e ottenere AuthResult.

  2. Ottieni il token di accesso per il provider Apple.

    Kotlin+KTX

    val oauthCredential: OAuthCredential =  authResult.credential
    val accessToken = oauthCredential.accessToken
    

    Java

    OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential();
    String accessToken = oauthCredential.getAccessToken();
    
  3. Revoca il token utilizzando l'API revokeAccessToken.

    Kotlin+KTX

    mAuth.revokeAccessToken(accessToken)
      .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
          // Access token successfully revoked
          // for the user ...
        }
    }
    

    Java

    mAuth.revokeAccessToken(accessToken)
        .addOnCompleteListener(this, new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
              if (task.isSuccessful()) {
                // Access token successfully revoked
                // for the user ...
              }
            }
      });
    
  1. Infine, elimina l'account utente (e tutti i dati associati).

    Passaggi successivi

    Dopo che un utente ha eseguito l'accesso per la prima volta, viene creato un nuovo account utente e collegato alle credenziali, ovvero nome utente e password, numero di telefono o informazioni del fornitore di autenticazione, con cui l'utente ha eseguito l'accesso. Questo nuovo viene archiviato come parte del progetto Firebase e può essere utilizzato per identificare a un utente in ogni app del progetto, a prescindere da come esegue l'accesso.

    • Nelle tue app, puoi ottenere le informazioni di base del profilo dell'utente dall'oggetto FirebaseUser. Vedi Gestire gli utenti.

    • In Firebase Realtime Database e Cloud Storage Regole di sicurezza, puoi ottieni l'ID utente unico dell'utente che ha eseguito l'accesso dalla variabile auth, e usarli per controllare i dati a cui un utente può accedere.

    Puoi consentire agli utenti di accedere alla tua app utilizzando più provider di autenticazione collegando le credenziali del provider di autenticazione a un account utente esistente.

    Per scollegare un utente, chiama signOut:

    Kotlin+KTX

    Firebase.auth.signOut()

    Java

    FirebaseAuth.getInstance().signOut();