Аутентификация с использованием Apple с JavaScript

Вы можете разрешить пользователям аутентифицироваться в Firebase, используя свой Apple ID, с помощью Firebase SDK, который обеспечивает сквозной процесс авторизации OAuth 2.0.

Прежде чем начать

Для авторизации пользователей с помощью Apple сначала настройте функцию «Вход с помощью Apple» на сайте разработчиков Apple, а затем включите Apple в качестве поставщика авторизации для вашего проекта Firebase.

Присоединяйтесь к программе для разработчиков Apple!

Настроить функцию «Вход с помощью Apple» могут только участники программы Apple Developer Program .

Настройка входа в систему с помощью Apple

На сайте Apple Developer выполните следующие действия:

  1. Свяжите свой веб-сайт с приложением, как описано в первом разделе раздела «Настройка входа в систему Apple для веб-браузера» . При появлении запроса зарегистрируйте следующий URL-адрес в качестве URL-адреса возврата:

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

    Идентификатор вашего проекта Firebase можно найти в > Вкладка «Общие» в консоли Firebase .

    После завершения запишите свой новый идентификатор службы (Service ID), который понадобится вам в следующем разделе.

  2. Создайте закрытый ключ для входа в систему с помощью Apple . В следующем разделе вам понадобятся ваш новый закрытый ключ и идентификатор ключа.
  3. Если вы используете какие-либо функции Firebase Authentication , отправляющие электронные письма пользователям, включая вход по ссылке из электронной почты, подтверждение адреса электронной почты, аннулирование изменений учетной записи и другие, настройте частную службу ретрансляции электронной почты Apple и зарегистрируйте noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (или домен вашего пользовательского шаблона электронной почты), чтобы Apple могла пересылать электронные письма, отправленные Firebase Authentication на анонимизированные адреса электронной почты Apple.

Включите Apple в качестве поставщика услуг авторизации.

  1. Добавьте Firebase в свой проект .
  2. В консоли Firebase перейдите в раздел Безопасность > Аутентификация .
  3. На вкладке «Метод входа» включите поставщика авторизации Apple . Укажите идентификатор службы, созданный вами в предыдущем разделе. Также в разделе конфигурации потока кода OAuth укажите свой идентификатор команды Apple, а также закрытый ключ и идентификатор ключа, созданные вами в предыдущем разделе.

Соблюдайте требования Apple к анонимизации данных.

Функция «Вход с Apple» предоставляет пользователям возможность анонимизировать свои данные, включая адрес электронной почты, при входе в систему. Пользователи, выбравшие этот вариант, имеют адреса электронной почты с доменом privaterelay.appleid.com . При использовании функции «Вход с Apple» в вашем приложении необходимо соблюдать все применимые правила и условия Apple для разработчиков, касающиеся этих анонимизированных Apple ID.

Это включает в себя получение необходимого согласия пользователя, прежде чем связывать какую-либо непосредственно идентифицирующую личную информацию с анонимизированным Apple ID. При использовании аутентификации Firebase это может включать следующие действия:

  • Привяжите адрес электронной почты к анонимизированному Apple ID или наоборот.
  • Привязать номер телефона к анонимизированному Apple ID или наоборот.
  • Привяжите неанонимные учетные данные из социальной сети (Facebook, Google и т. д.) к анонимизированному Apple ID или наоборот.

Приведенный выше список не является исчерпывающим. Чтобы убедиться, что ваше приложение соответствует требованиям Apple, ознакомьтесь с Лицензионным соглашением программы Apple для разработчиков в разделе «Участие» вашей учетной записи разработчика.

Обрабатывайте процесс авторизации с помощью Firebase SDK.

Если вы разрабатываете веб-приложение, самый простой способ аутентифицировать пользователей в Firebase с помощью их учетных записей Apple — это обработать весь процесс входа в систему с помощью Firebase JavaScript SDK.

Для обработки процесса авторизации с помощью Firebase JavaScript SDK выполните следующие действия:

  1. Создайте экземпляр OAuthProvider , используя соответствующий идентификатор поставщика apple.com .

    Web

    import { OAuthProvider } from "firebase/auth";
    
    const provider = new OAuthProvider('apple.com');

    Web

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. Необязательно: укажите дополнительные области действия OAuth 2.0, помимо области по умолчанию, которые вы хотите запросить у поставщика аутентификации.

    Web

    provider.addScope('email');
    provider.addScope('name');

    Web

    provider.addScope('email');
    provider.addScope('name');

    По умолчанию, если включена опция «Одна учетная запись на один адрес электронной почты» , Firebase запрашивает области действия для электронной почты и имени. Если вы измените этот параметр на «Несколько учетных записей на один адрес электронной почты» , Firebase не будет запрашивать у Apple никаких областей действия, если вы их не укажете.

  3. Необязательно: если вы хотите отображать экран входа в систему Apple на языке, отличном от английского, задайте параметр locale . Список поддерживаемых языковых версий см . в документации по входу в систему с помощью Apple .

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. Аутентификация в Firebase осуществляется с помощью объекта поставщика OAuth. Вы можете предложить пользователям войти в систему с помощью своих учетных записей Apple, открыв всплывающее окно или перенаправив их на страницу входа. На мобильных устройствах предпочтительнее использовать метод перенаправления.

    • Для входа в систему через всплывающее окно вызовите метод signInWithPopup() :

      Web

      import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
      
      const auth = getAuth();
      signInWithPopup(auth, provider)
        .then((result) => {
          // The signed-in user info.
          const user = result.user;
      
          // Apple credential
          const credential = OAuthProvider.credentialFromResult(result);
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
          // ...
        })
        .catch((error) => {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const email = error.customData.email;
          // The credential that was used.
          const credential = OAuthProvider.credentialFromError(error);
      
          // ...
        });

      Web

      firebase
        .auth()
        .signInWithPopup(provider)
        .then((result) => {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
      
          // The signed-in user info.
          var user = result.user;
      
          // You can also get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
        // ...
        })
        .catch((error) => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
      
          // ...
        });
    • Для входа в систему с перенаправлением на страницу авторизации вызовите signInWithRedirect() :

    При использовании signInWithRedirect , linkWithRedirect или reauthenticateWithRedirect следует придерживаться лучших практик .

    Web

    import { getAuth, signInWithRedirect } from "firebase/auth";
    
    const auth = getAuth();
    signInWithRedirect(auth, provider);

    Web

    firebase.auth().signInWithRedirect(provider);

    После того, как пользователь завершит вход в систему и вернется на страницу, вы можете получить результат входа, вызвав getRedirectResult() :

    Web

    import { getAuth, getRedirectResult, OAuthProvider } from "firebase/auth";
    
    // Result from Redirect auth flow.
    const auth = getAuth();
    getRedirectResult(auth)
      .then((result) => {
        const credential = OAuthProvider.credentialFromResult(result);
        if (credential) {
          // You can also get the Apple OAuth Access and ID Tokens.
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
        }
        // The signed-in user info.
        const user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The credential that was used.
        const credential = OAuthProvider.credentialFromError(error);
    
        // ...
      });

    Web

    // Result from Redirect auth flow.
    firebase
      .auth()
      .getRedirectResult()
      .then((result) => {
        if (result.credential) {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
    
          // You can get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
    
          // IdP data available in result.additionalUserInfo.profile.
          // ...
        }
        // The signed-in user info.
        var user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        // The email of the user's account used.
        var email = error.email;
        // The firebase.auth.AuthCredential type that was used.
        var credential = error.credential;
    
        // ...
      });

    Здесь же можно перехватывать и обрабатывать ошибки. Список кодов ошибок см. в справочнике API .

    В отличие от других провайдеров, поддерживаемых Firebase Auth, Apple не предоставляет URL-адрес фотографии.

    Кроме того, если пользователь решает не делиться своим адресом электронной почты с приложением, Apple создает для этого пользователя уникальный адрес электронной почты (в формате xyz@privaterelay.appleid.com ), который затем передается вашему приложению. Если вы настроили службу пересылки частной электронной почты, Apple пересылает электронные письма, отправленные на анонимизированный адрес, на реальный адрес электронной почты пользователя.

    Apple передает информацию о пользователе, такую ​​как отображаемое имя, приложениям только при первом входе пользователя в систему. Обычно Firebase сохраняет отображаемое имя при первом входе пользователя в систему через Apple, и его можно получить с помощью firebase.auth().currentUser.displayName . Однако, если вы ранее использовали Apple для входа пользователя в приложение без Firebase, Apple не предоставит Firebase отображаемое имя пользователя.

Повторная аутентификация и привязка учетных записей

Аналогичный подход можно использовать с reauthenticateWithPopup() и reauthenticateWithRedirect() , которые позволяют получить новые учетные данные для выполнения конфиденциальных операций, требующих недавнего входа в систему:

Web

import { getAuth, reauthenticateWithPopup, OAuthProvider } from "firebase/auth";

// Result from Redirect auth flow.
const auth = getAuth();
const provider = new OAuthProvider('apple.com');

reauthenticateWithPopup(auth.currentUser, provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.

    // The signed-in user info.
    const user = result.user;

    // You can also get the Apple OAuth Access and ID Tokens.
    const credential = OAuthProvider.credentialFromResult(result);
    const accessToken = credential.accessToken;
    const idToken = credential.idToken;

    // ...
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The credential that was used.
    const credential = OAuthProvider.credentialFromError(error);

    // ...
  });

Web

const provider = new firebase.auth.OAuthProvider('apple.com');

firebase
  .auth()
  .currentUser
  .reauthenticateWithPopup(provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.
    /** @type {firebase.auth.OAuthCredential} */
    var credential = result.credential;

    // The signed-in user info.
    var user = result.user;
     // You can also get the Apple OAuth Access and ID Tokens.
    var accessToken = credential.accessToken;
    var idToken = credential.idToken;

    // IdP data available in result.additionalUserInfo.profile.
      // ...
  })
  .catch((error) => {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    // The email of the user's account used.
    var email = error.email;
    // The firebase.auth.AuthCredential type that was used.
    var credential = error.credential;

    // ...
  });

Кроме того, вы можете использовать linkWithPopup() и linkWithRedirect() для привязки различных поставщиков идентификации к существующим учетным записям.

Обратите внимание, что Apple требует получения явного согласия пользователей, прежде чем связывать их учетные записи Apple с другими данными.

Например, чтобы связать учетную запись Facebook с текущей учетной записью Firebase, используйте токен доступа, полученный при входе пользователя в Facebook:

Web

import { getAuth, linkWithPopup, FacebookAuthProvider } from "firebase/auth";

const auth = getAuth();
const provider = new FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
linkWithPopup(auth.currentUser, provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // ...

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Web

const provider = new firebase.auth.FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
firebase.auth().currentUser.linkWithPopup(provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // Facebook additional data available in result.additionalUserInfo.profile,

      // Additional Facebook OAuth access token can also be retrieved.
      // result.credential.accessToken

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Аутентификация с помощью Firebase в расширении Chrome

Если вы разрабатываете расширение для Chrome, ознакомьтесь с руководством по работе с документами, отображаемыми вне экрана .

Обратите внимание, что вам по-прежнему необходимо подтвердить пользовательский домен в Apple, аналогично тому, как это делается для домена firebaseapp.com по умолчанию:

http://auth.custom.example.com/.well-known/apple-developer-domain-association.txt

аннулирование токена

Компания Apple требует, чтобы приложения, поддерживающие создание учетных записей, позволяли пользователям инициировать удаление своей учетной записи внутри приложения, как описано в рекомендациях по проверке приложений в App Store.

Для выполнения этого требования выполните следующие шаги:

  1. Убедитесь, что вы заполнили раздел « Идентификатор службы и конфигурация потока кода OAuth» в настройках поставщика «Вход с помощью Apple», как описано в разделе «Настройка входа с помощью Apple» .

  2. Поскольку Firebase не хранит пользовательские токены при создании пользователей с использованием входа через Apple Sign-in, перед аннулированием токена и удалением учетной записи необходимо попросить пользователя войти в систему еще раз.

    Затем получите токен доступа Apple OAuth из объекта OAuthCredential и используйте его для вызова revokeAccessToken(auth, token) чтобы отозвать токен доступа Apple OAuth.

    const provider = new OAuthProvider('apple.com');
    provider.addScope('email');
    provider.addScope('name');
    
    const auth = getAuth();
    signInWithPopup(auth, provider).then(result => {
      // Get the Apple OAuth access token.
      const credential = OAuthProvider.credentialFromResult(result);
      const accessToken = credential.accessToken;
    
      // Revoke the Apple OAuth access token.
      revokeAccessToken(auth, accessToken)
        .then(() => {
          // Token revoked.
    
          // Delete the user account.
          // ...
        })
        .catch(error => {
          // An error happened.
          // ...
        });
    });
    
  3. Наконец, удалите учетную запись пользователя (и все связанные с ней данные).

Расширенные возможности: аутентификация с помощью Firebase в Node.js

Для аутентификации в Firebase в приложении Node.js:

  1. Войдите в систему пользователя с помощью его учетной записи Apple и получите токен Apple ID пользователя. Это можно сделать несколькими способами. Например, если ваше приложение Node.js имеет браузерный интерфейс:

    1. На вашем бэкэнде сгенерируйте случайную строку («nonce») и вычислите её хеш SHA256. Nonce — это одноразовое значение, которое используется для проверки одного цикла обмена данными между вашим бэкэндом и серверами аутентификации Apple.

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = (length) => {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        let nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = function(length) {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        var nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');
    2. На странице входа укажите хешированный nonce в настройках входа через Apple:

      <script src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
      <div id="appleid-signin" data-color="black" data-border="true" data-type="sign in"></div>
      <script>
          AppleID.auth.init({
              clientId: YOUR_APPLE_CLIENT_ID,
              scope: 'name email',
              redirectURI: URL_TO_YOUR_REDIRECT_HANDLER,  // See the next step.
              state: '[STATE]',  // Optional value that Apple will send back to you
                                 // so you can return users to the same context after
                                 // they sign in.
              nonce: HASHED_NONCE  // The hashed nonce you generated in the previous step.
          });
      </script>
      
    3. Получите токен Apple ID из ответа на запрос аутентификации, отправленного методом POST, на стороне сервера:

      app.post('/redirect', (req, res) => {
        const savedState = req.cookies.__session;
        const code = req.body.code;
        const state = req.body.state;
        const appleIdToken = req.body.id_token;
        if (savedState !== state || !code) {
          res.status(403).send('403: Permission denied');
        } else {
          // Sign in with Firebase using appleIdToken. (See next step).
        }
      });
      

    См. также раздел «Настройка веб-страницы для входа через Apple» .

  2. Получив токен Apple ID пользователя, используйте его для создания объекта Credential, а затем войдите в систему, используя эти учетные данные:

    Web

    import { getAuth, signInWithCredential, OAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    
    // Build Firebase credential with the Apple ID token.
    const provider = new OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    signInWithCredential(auth, authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

    Web

    // Build Firebase credential with the Apple ID token.
    const provider = new firebase.auth.OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    firebase.auth().signInWithCredential(authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

Следующие шаги

После первого входа пользователя в систему создается новая учетная запись, которая связывается с учетными данными — то есть именем пользователя и паролем, номером телефона или информацией о поставщике аутентификации, — которые пользователь использовал для входа. Эта новая учетная запись хранится как часть вашего проекта Firebase и может использоваться для идентификации пользователя во всех приложениях вашего проекта, независимо от способа входа пользователя.

  • В ваших приложениях рекомендуемый способ узнать статус авторизации пользователя — установить наблюдателя для объекта Auth . Затем вы можете получить основную информацию профиля пользователя из объекта User . См. раздел «Управление пользователями» .

  • В правилах безопасности Firebase Realtime Database и Cloud Storage вы можете получить уникальный идентификатор пользователя, вошедшего в систему, из переменной auth и использовать его для управления доступом пользователя к данным.

Вы можете разрешить пользователям входить в ваше приложение, используя несколько поставщиков аутентификации, связав учетные данные поставщика аутентификации с существующей учетной записью пользователя.

Для выхода из системы пользователя вызовите signOut :

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});