Bạn có thể cho phép người dùng xác thực bằng Firebase bằng Apple ID 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 từ đầu đến cuối.
Trước khi bắt đầu
Để đăng nhập 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 của bạn.
Tham gia Chương trình Nhà phát triển của Apple
Chỉ thành viên của Chương trình Nhà phát triển của Apple mới có thể thiết lập tính năng Đăng nhập bằng Apple.
Định cấu hình tính năng Đăng nhập bằng Apple
Trên trang web Apple Developer, hãy làm như sau:
-
Liên kết trang web với ứng dụng của bạn như mô tả trong phần đầu tiên của bài viết Đị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 chuyển hướng:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
Bạn có thể lấy mã dự án Firebase trên trang cài đặt bảng điều khiển Firebase.
Sau khi hoàn tất, hãy ghi lại mã nhận dạng dịch vụ mới. Bạn sẽ cần mã này trong phần tiếp theo.
- Tạo khoá riêng tư Đăng nhập bằng Apple. Bạn sẽ cần khoá riêng tư và mã khoá mới trong phần tiếp theo.
-
Nếu bạn sử dụng bất kỳ tính năng nào của Firebase Authentication để gửi email cho người dùng, bao gồm cả tính năng đăng nhập bằng đường liên kết qua email, xác minh địa chỉ email, thu hồi thay đổi tài khoản và các tính năng khác, hãy định cấu hình dịch vụ chuyển tiếp email riêng tư của Apple và đă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 email do Firebase Authentication gửi đến địa chỉ email ẩn danh của Apple.
Bật Apple làm nhà cung cấp dịch vụ đăng nhập
- Thêm Firebase vào dự án của bạn.
- Trong bảng điều khiển Firebase, hãy mở mục Auth (Xác thực). Trên thẻ Phương thức đăng nhập, hãy bật trình cung cấp Apple. Chỉ định Mã dịch vụ mà bạn đã tạo trong phần trước. Ngoài ra, trong phần cấu hình luồng mã OAuth, hãy chỉ định mã nhóm Apple, khoá riêng tư và mã khoá mà bạn đã tạo trong 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 hoá dữ liệu của họ, bao gồm cả địa chỉ email, khi đăng nhập. Người dùng chọn lựa chọn này sẽ có địa chỉ email có miền privaterelay.appleid.com
. Khi 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 hoặc điều khoản hiện hành của Apple dành cho nhà phát triển liên quan đến các Apple ID ẩn danh này.
Điều này bao gồm việc lấy mọi sự đồng ý bắt buộc của người dùng trước khi bạn liên kết bất kỳ thông tin nhận dạng cá nhân trực tiếp nào với một Apple ID ẩn danh. Khi sử dụng Xác thực Firebase, điều này có thể bao gồm các thao tác sau:
- Liên kế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 một ID Apple ẩn danh hoặc ngược lại
- Liên kết thông tin đăng nhập mạng 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 đủ. Hãy tham khảo Thoả thuận cấp phép của Chương trình dành cho nhà phát triển của Apple trong phần Thành viên của tài khoản nhà phát triể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 đang tạo một ứng dụng web, cách dễ nhất để xác thực người dùng bằng Firebase thông qua tài khoản Apple của họ là 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 Firebase JavaScript SDK, hãy làm theo các bước sau:
Tạo một phiên bản OAuthProvider bằng cách sử dụng mã nhận dạng nhà cung cấp tương ứng apple.com.
Web
import { OAuthProvider } from "firebase/auth"; const provider = new OAuthProvider('apple.com');
Web
var provider = new firebase.auth.OAuthProvider('apple.com');
Không bắt buộc: Chỉ định các phạm vi OAuth 2.0 bổ sung ngoài phạm vi mặc định mà bạn muốn yêu cầu từ trình 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 sẽ yêu cầu các phạm vi email và tên. Nếu bạn thay đổi chế độ cài đặt này thành Nhiều tài khoản cho mỗi địa chỉ email, Firebase sẽ không yêu cầu bất kỳ phạm vi nào từ Apple, trừ phi bạn chỉ định các phạm vi đó.
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ác ngoài tiếng Anh, hãy đặt tham số
locale
. Hãy xem tài liệu Đăng nhập bằng Apple để biết 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' });
Xác thực bằng Firebase thông qua đối tượng trì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 chuyển hướng đến trang đăng nhập. Bạn nên dùng phương thức chuyển hướng 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ặcreauthenticateWithRedirect
.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 quy trình đă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ý lỗi. Để biết danh sách mã lỗi, hãy xem tài liệu tham khảo về API.
Không giống như các nhà cung cấp khác được Firebase Auth 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 sẽ cung cấp một địa chỉ email duy nhất cho người dùng đó (theo dạng
xyz@privaterelay.appleid.com
) và chia sẻ địa chỉ này 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 sẽ chuyển tiếp email gửi đến địa chỉ ẩn danh đến đị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 vào lần đầu tiên người dùng đăng nhập. Thông thường, Firebase sẽ lưu trữ tên hiển thị vào lần đầu tiên người dùng đăng nhập bằng Apple. Bạn có thể lấy tên hiển thị này bằng
firebase.auth().currentUser.displayName
. Tuy nhiên, nếu trước đây bạn dùng Apple để đăng nhập người dùng vào ứng dụng mà không dùng Firebase, thì Apple sẽ không cung cấp tên hiển thị của người dùng cho Firebase.
Xác thực lại và liên kết tài khoản
Bạn có thể sử dụng cùng một mẫu với reauthenticateWithPopup()
và reauthenticateWithRedirect()
. Bạn có thể dùng các mẫu này để truy xuất thông tin đăng nhập mới cho các thao tác 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ể dùng linkWithPopup()
và linkWithRedirect()
để liên kết các nhà cung cấp danh tính khác nhau với các tài khoản hiện có.
Xin lưu ý rằng Apple yêu cầu bạn phải nhận được sự đồng ý rõ ràng của người dùng trước khi liên kết tài khoản Apple của họ với dữ liệu khác.
Ví dụ: để liên kết mộ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 khi đă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 tiện ích Chrome
Nếu bạn đang tạo một ứng dụng tiện ích 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 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 những ứng dụng hỗ trợ quy trình 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ọ ngay trong ứng dụng, như mô tả trong Nguyên tắc đánh giá của App Store
Để đáp ứng yêu cầu này, hãy triển khai các bước sau:
Đảm bảo bạn đã điền vào phần Mã nhận dạng dịch vụ và Cấu hình quy trình mã OAuth của cấu hình nhà cung cấp Đăng nhập bằng Apple, như mô tả trong phần Định cấu hình tính năng Đăng nhập bằng Apple.
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 tính năng Đăng nhập bằng Apple, nên 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 đó, hãy lấy mã truy cập Apple OAuth từ
OAuthCredential
và dùng mã này để gọirevokeAccessToken(auth, token)
nhằm thu hồi mã truy cập 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. // ... }); });
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:
Đăng nhập người dùng bằng Tài khoản Apple của họ và nhận mã thông báo Apple ID của người dùng. Bạn có thể thực hiện việc này theo nhiều cách. Ví dụ: nếu ứng dụng Node.js của bạn có giao diện người dùng trình duyệt:
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 của chuỗi đó. Số chỉ dùng một lần là giá trị dùng một lần mà bạn sử dụng để xác thực một chuyến khứ hồi duy nhất giữa máy chủ phụ trợ của bạn và máy chủ uỷ quyền 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');
Trên trang đăng nhập, hãy chỉ định số chỉ dùng một lần đã băm trong cấu hình Đăng nhập bằng 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>
Nhận mã thông báo Apple ID từ phía máy chủ phản hồi xác thực được đăng:
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 bài viết Định cấu hình trang web để sử dụng tính năng Đăng nhập bằng Apple.
Sau khi bạn nhận được mã thông báo Apple ID của người dùng, hãy dùng mã thông báo đó để tạo một đối tượng Credential rồi đăng nhập người dùng bằng thông tin xác thực đó:
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à 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 hoặc thông tin nhà cung cấp dịch vụ uỷ quyền) mà người dùng đã đăng nhập. Tài khoản mới này được lưu trữ trong dự án Firebase của bạn và có thể dùng để xác định một người dùng trên mọi ứng dụng trong dự án, bất kể người dùng đăng nhập bằng cách nào.
-
Trong các ứng dụng của bạn, cách hay nhất để biết trạng thái uỷ quyền của người dùng là đặt một đối tượng theo dõi trên đối tượng
Auth
. Sau đó, bạn có thể lấy thông tin hồ sơ cơ bản của người dùng từ đối tượngUser
. Xem phần Quản lý người dùng. Trong Firebase Realtime Database 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 riêng biệt của người dùng đã đăng nhập từ biến
auth
và dùng mã nhận dạng này để kiểm soát 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 bạn bằng nhiều trình cung cấp dịch vụ xác thực bằng cách liên kết thông tin đăng nhập của trình cung cấp dịch vụ xác thực với một tài khoản người dùng hiện có.
Để đăng xuấ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. });