Xác thực bằng Apple với JavaScript

Bạn có thể cho phép người dùng xác thực bằng Firebase bằng ID Apple của họ bằng cách sử dụng Firebase SDK để thực hiện quy trình đăng nhập OAuth 2.0 toàn diện.

Trước khi bắt đầu

Để đăng nhập cho người dùng bằng Apple, trước tiên hãy định cấu hình tính năng Đăng nhập bằng Apple trên trang web dành cho nhà phát triển của Apple, sau đó bật Apple làm nhà cung cấp dịch vụ đăng nhập cho Dự án Firebase.

Tham gia Chương trình nhà phát triển của Apple

Chỉ thành viên của Nhà phát triển Apple mới có thể định cấu hình cho tính năng Đăng nhập bằng Apple Chương trình.

Định cấu hình tính năng Đăng nhập bằng Apple

Trên Apple dành cho nhà phát triển, hãy làm như sau:

  1. Liên kết trang web với ứng dụng của bạn như mô tả trong phần đầu tiên trong tổng số Định cấu hình tính năng Đăng nhập bằng Apple cho web. Khi được nhắc, hãy đăng ký URL sau đây làm URL trả lại:

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

    Bạn có thể nhận được mã dự án Firebase trên trang Bảng điều khiển của Firebase cài đặt.

    Khi bạn hoàn tất, hãy ghi lại Mã dịch vụ mới để có trong phần tiếp theo.

  2. Tạo một Đăng nhập bằng khoá riêng tư của Apple. Bạn sẽ cần khoá riêng tư và khoá mới mã nhận dạng trong phần tiếp theo.
  3. Nếu bạn sử dụng bất kỳ tính năng nào của Xác thực Firebase gửi email cho người dùng, bao gồm đăng nhập bằng đường liên kết email, xác minh địa chỉ email, thay đổi tài khoản thu hồi và khác, định cấu hình dịch vụ chuyển tiếp email riêng tư của Apple rồi đăng ký noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (hoặc miền mẫu email tuỳ chỉnh của bạn) để Apple có thể chuyển tiếp các email được gửi bằng tính năng Xác thực Firebase sang địa chỉ email Apple ẩn danh.

Cho phép Apple làm nhà cung cấp dịch vụ đăng nhập

  1. Thêm Firebase vào dự án của bạn.
  2. Trong bảng điều khiển của Firebase, hãy mở phần Xác thực. Trên thẻ Phương thức đăng nhập, bật nhà cung cấp Apple. Chỉ định Mã dịch vụ mà bạn đã tạo ở phần trước. Ngoài ra, trong Phần cấu hình luồng mã OAuth, chỉ định ID nhóm Apple của bạn và khoá cá nhân và mã nhận dạng khoá mà bạn đã tạo ở phần trước.

Tuân thủ các yêu cầu của Apple về dữ liệu ẩn danh

Tính năng Đăng nhập bằng Apple cho phép người dùng ẩn danh dữ liệu của họ, bao gồm cả địa chỉ email của họ khi đăng nhập. Người dùng chọn phương án này có địa chỉ email với miền privaterelay.appleid.com. Thời gian bạn sử dụng tính năng Đăng nhập bằng Apple trong ứng dụng của mình, bạn phải tuân thủ mọi chính sách dành cho nhà phát triển hoặc điều khoản của Apple liên quan đến Mã nhận dạng.

Điều này bao gồm cả việc có được sự đồng ý cần thiết của người dùng trước khi bạn liên kết mọi thông tin cá nhân nhận dạng trực tiếp với Apple ẩn danh Mã nhận dạng. Khi sử dụng tính năng Xác thực Firebase, điều này có thể bao gồm những điều sau hành động:

  • Liên kết một địa chỉ email với một ID Apple ẩn danh hoặc ngược lại.
  • Liên kết số điện thoại với ID Apple ẩn danh hoặc ngược lại
  • Liên kết thông tin đăng nhập xã hội không ẩn danh (Facebook, Google, v.v.) với một Apple ID ẩn danh hoặc ngược lại.

Danh sách bên trên chưa đầy đủ. Tham khảo Chương trình dành cho nhà phát triển của Apple Thoả thuận cấp phép trong phần Thành viên của tài khoản nhà phát triển để thực hiện đảm bảo ứng dụng của bạn đáp ứng các yêu cầu của Apple.

Xử lý quy trình đăng nhập bằng Firebase SDK

Nếu bạn đang tạo một ứng dụng web thì cách dễ nhất để xác thực người dùng với Firebase sử dụng tài khoản Apple để xử lý toàn bộ quy trình đăng nhập bằng Firebase JavaScript SDK.

Để xử lý quy trình đăng nhập bằng SDK JavaScript của Firebase, hãy làm theo các bước sau các bước:

  1. Tạo một thực thể của OAuthProvider bằng cách sử dụng mã nhà cung cấp apple.com.

    Web

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

    Web

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. Không bắt buộc: Chỉ định các phạm vi OAuth 2.0 khác ngoài phạm vi mặc định mà bạn muốn yêu cầu từ nhà cung cấp dịch vụ xác thực.

    Web

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

    Web

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

    Theo mặc định, khi bạn bật chế độ Một tài khoản cho mỗi địa chỉ email, Firebase yêu cầu phạm vi tên và email. Nếu bạn thay đổi cài đặt này thành Nhiều tài khoản cho mỗi địa chỉ email, Firebase không yêu cầu bất kỳ phạm vi nào từ Apple trừ khi bạn chỉ định chúng.

  3. Không bắt buộc: Nếu bạn muốn hiển thị màn hình đăng nhập của Apple bằng một ngôn ngữ không phải tiếng Anh, hãy đặt thông số locale. Xem Đăng nhập bằng Tài liệu của Apple cho các ngôn ngữ được hỗ trợ.

    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. Xác thực bằng Firebase bằng đối tượng nhà cung cấp OAuth. Bạn có thể nhắc người dùng đăng nhập bằng Tài khoản Apple của họ bằng cách mở cửa sổ bật lên hoặc bằng cách chuyển hướng đến trang đăng nhập. Phương thức chuyển hướng là được ưu tiên trên thiết bị di động.

    • Để đăng nhập bằng cửa sổ bật lên, hãy gọi 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;
      
          // ...
        });
    • Để đăng nhập bằng cách chuyển hướng đến trang đăng nhập, hãy gọi signInWithRedirect():

    Làm theo các phương pháp hay nhất khi sử dụng signInWithRedirect, linkWithRedirect hoặc reauthenticateWithRedirect.

    Web

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

    Web

    firebase.auth().signInWithRedirect(provider);

    Sau khi người dùng hoàn tất đăng nhập và quay lại trang, bạn có thể nhận được kết quả đăng nhập bằng cách gọi 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;
    
        // ...
      });

    Đây cũng là nơi bạn có thể phát hiện và xử lý các lỗi. Để xem danh sách lỗi các mã, hãy xem Tài liệu tham khảo API.

    Không giống như các nhà cung cấp khác được tính năng Xác thực Firebase hỗ trợ, Apple không cung cấp URL ảnh.

    Ngoài ra, khi người dùng chọn không chia sẻ email của họ với ứng dụng, Apple cấp một địa chỉ email duy nhất cho người dùng đó (theo biểu mẫu xyz@privaterelay.appleid.com) mà hệ thống này chia sẻ với ứng dụng của bạn. Nếu bạn đã định cấu hình dịch vụ chuyển tiếp email riêng tư, Apple chuyển tiếp các email được gửi đến địa chỉ ẩn danh sang địa chỉ email thực của người dùng.

    Apple chỉ chia sẻ thông tin người dùng (chẳng hạn như tên hiển thị) với các ứng dụng khi người dùng đăng nhập lần đầu tiên. Thông thường, Firebase lưu trữ tên hiển thị lần đầu tiên người dùng đăng nhập bằng Apple. Bạn có thể tải tính năng này bằng firebase.auth().currentUser.displayName. Tuy nhiên, nếu trước đây bạn đã sử dụng Apple để đăng nhập người dùng vào ứng dụng mà không nếu bạn sử dụng Firebase, Apple sẽ không cung cấp cho Firebase tên hiển thị của người dùng.

Xác thực lại và liên kết tài khoản

Bạn có thể dùng cùng một mẫu cho reauthenticateWithPopup()reauthenticateWithRedirect() mà bạn có thể sử dụng để truy xuất thông tin đăng nhập cho các hoạt động nhạy cảm yêu cầu đăng nhập gần đây:

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;

    // ...
  });

Ngoài ra, bạn có thể sử dụng linkWithPopup()linkWithRedirect() để liên kết các nhà cung cấp danh tính đến các tài khoản hiện có.

Xin lưu ý rằng Apple yêu cầu bạn phải được người dùng đồng ý rõ ràng trước khi liên kết tài khoản Apple của họ sang các dữ liệu khác.

Ví dụ: để liên kết tài khoản Facebook với tài khoản Firebase hiện tại, hãy sử dụng mã truy cập mà bạn nhận được từ việc đăng nhập người dùng vào 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.
    });

Xác thực bằng Firebase trong một tiện ích của Chrome

Nếu bạn đang xây dựng ứng dụng tiện ích của Chrome, hãy xem Hướng dẫn về tài liệu ngoài màn hình

Xin lưu ý rằng bạn vẫn phải xác minh miền tuỳ chỉnh với Apple theo cách tương tự như miền firebaseapp.com mặc định:

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

Thu hồi mã thông báo

Apple yêu cầu các ứng dụng hỗ trợ tạo tài khoản phải cho phép người dùng bắt đầu xoá tài khoản của họ trong ứng dụng, như mô tả trong phần Bài đánh giá của cửa hàng ứng dụng Nguyên tắc

Để đáp ứng yêu cầu này, hãy triển khai các bước sau:

  1. Đảm bảo bạn đã điền vào Mã dịch vụquy trình mã OAuth cấu hình của phần cấu hình của nhà cung cấp Đăng nhập bằng Apple, như nêu trong phần Định cấu hình đăng nhập bằng Apple .

  2. Vì Firebase không lưu trữ mã thông báo người dùng khi người dùng được tạo bằng Khi đăng nhập bằng Apple, bạn phải yêu cầu người dùng đăng nhập lại trước khi thu hồi mã thông báo của họ và xoá tài khoản.

    Sau đó, lấy mã truy cập OAuth của Apple từ OAuthCredential và dùng đường liên kết này để gọi revokeAccessToken(auth, token) nhằm thu hồi OAuth của Apple mã truy cập.

    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. Cuối cùng, hãy xoá tài khoản người dùng (và tất cả dữ liệu liên quan).

Nâng cao: Xác thực bằng Firebase trong Node.js

Cách xác thực bằng Firebase trong ứng dụng Node.js:

  1. Đăng nhập người dùng bằng Tài khoản Apple của họ và nhận ID Apple của người dùng mã thông báo. Bạn có thể thực hiện việc này theo một số cách. Ví dụ: nếu Node.js của bạn ứng dụng có giao diện người dùng của trình duyệt:

    1. Trên phần phụ trợ, hãy tạo một chuỗi ngẫu nhiên ("số chỉ dùng một lần") và tính toán Hàm băm SHA256. Số chỉ dùng một lần là giá trị dùng một lần mà bạn dùng để xác thực một một lượt khứ hồi giữa phần phụ trợ của bạn và máy chủ xác thực của 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. Trên trang đăng nhập, hãy chỉ định số chỉ dùng một lần đã băm trong mục Đăng nhập bằng Cấu hình của 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. Lấy mã thông báo ID Apple từ phản hồi xác thực đã POST ở phía máy chủ:

      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).
        }
      });
      

    Ngoài ra, hãy xem Định cấu hình trang web của bạn để đăng nhập bằng Apple.

  2. Sau khi bạn nhận được mã thông báo ID Apple của người dùng, hãy sử dụng mã đó để tạo Chứng chỉ danh tính rồi đăng nhập vào người dùng bằng thông tin đăng nhập:

    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);
      });

Các bước tiếp theo

Sau khi người dùng đăng nhập lần đầu tiên, một tài khoản người dùng mới sẽ được tạo và được liên kết với thông tin đăng nhập—tức là tên người dùng và mật khẩu, số điện thoại số hoặc thông tin của nhà cung cấp dịch vụ xác thực – người dùng đã đăng nhập. Thông tin mới này được lưu trữ như một phần của dự án Firebase và có thể được dùng để xác định một người dùng trên mọi ứng dụng trong dự án của bạn, bất kể người dùng đăng nhập bằng cách nào.

  • Trong ứng dụng của mình, bạn nên sử dụng cách để biết trạng thái xác thực của người dùng đặt trình quan sát trên đối tượng Auth. Sau đó, bạn có thể lấy thông tin thông tin hồ sơ cơ bản qua đối tượng User. Xem Quản lý người dùng.

  • Trong Cơ sở dữ liệu theo thời gian thực của Firebase và Cloud Storage Quy tắc bảo mật, bạn có thể lấy mã nhận dạng người dùng duy nhất của người dùng đã đăng nhập từ biến auth, để kiểm soát loại dữ liệu mà người dùng có thể truy cập.

Bạn có thể cho phép người dùng đăng nhập vào ứng dụng của mình bằng nhiều phương thức xác thực bằng cách liên kết thông tin đăng nhập của nhà cung cấp dịch vụ xác thực với tài khoản người dùng hiện có.

Để đăng xuất một người dùng, hãy gọi 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.
});