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 Android. Hãy nhớ đăng ký chữ ký SHA-1 của ứng dụng khi bạn thiết lập ứng dụng trong bảng điều khiển Firebase.
- 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
Trên Android, 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 Android SDK.
Để xử lý quy trình đăng nhập bằng Firebase Android SDK, hãy làm theo các bước sau:
Tạo một phiên bản của
OAuthProvider
bằng Trình tạo của phiên bản đó với mã nhận dạng nhà cung cấpapple.com
:Kotlin
val provider = OAuthProvider.newBuilder("apple.com")
Java
OAuthProvider.Builder provider = OAuthProvider.newBuilder("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.
Kotlin
provider.setScopes(arrayOf("email", "name"))
Java
List<String> scopes = new ArrayList<String>() { { add("email"); add("name"); } }; provider.setScopes(scopes);
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ợ.Kotlin
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr")
Java
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr");
Xác thực bằng Firebase thông qua đối tượng trình cung cấp OAuth. Xin lưu ý rằng không giống như các thao tác
FirebaseAuth
khác, thao tác này sẽ kiểm soát giao diện người dùng của bạn bằng cách mở một Thẻ Chrome tuỳ chỉnh. Do đó, đừng tham chiếu Hoạt động của bạn trongOnSuccessListener
vàOnFailureListener
mà bạn đính kèm vì chúng sẽ tách ra ngay lập tức khi thao tác bắt đầu giao diện người dùng.Trước tiên, bạn nên kiểm tra xem mình đã nhận được phản hồi hay chưa. Việc đăng nhập bằng phương thức này sẽ đặt Hoạt động của bạn ở chế độ nền, tức là hệ thống có thể khôi phục Hoạt động đó trong quy trình đăng nhập. Để đảm bảo rằng bạn không khiến người dùng thử lại nếu điều này xảy ra, bạn nên kiểm tra xem đã có kết quả hay chưa.
Để kiểm tra xem có kết quả đang chờ xử lý hay không, hãy gọi
getPendingAuthResult()
:Kotlin
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"); }
Nếu không có kết quả đang chờ xử lý, hãy bắt đầu quy trình đăng nhập bằng cách gọi
startActivityForSignInWithProvider()
:Kotlin
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); } });
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 được 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
getCurrentUser().getDisplayName()
. 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ể dùng cùng một mẫu với startActivityForReauthenticateWithProvider()
mà bạn có thể dùng để 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:
Kotlin
// 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.
}
});
Ngoài ra, bạn có thể dùng linkWithCredential()
để 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:
Kotlin
// 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.
}
}
});
Nâng cao: Xử lý quy trình đăng nhập theo cách thủ công
Bạn cũng có thể xác thực bằng Firebase thông qua Tài khoản Apple bằng cách xử lý quy trình đăng nhập bằng cách sử dụng Apple Sign-In JS SDK, tự tạo quy trình OAuth hoặc bằng cách sử dụng một thư viện OAuth như AppAuth.
Đối với mỗi yêu cầu đăng nhập, hãy tạo một chuỗi ngẫu nhiên (một "số chỉ dùng một lần") mà bạn sẽ dùng để đảm bảo mã thông báo nhận dạng bạn nhận được được cấp cụ thể để phản hồi yêu cầu xác thực của ứng dụng. Bước này rất quan trọng để ngăn chặn các cuộc tấn công phát lại.
Bạn có thể tạo một số chỉ dùng một lần an toàn về mật mã trên Android bằng
SecureRandom
, như trong ví dụ sau:Kotlin
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(); }
Sau đó, lấy hàm băm SHA256 của số chỉ dùng một lần dưới dạng một chuỗi hex:
Kotlin
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(); }
Bạn sẽ gửi hàm băm SHA256 của giá trị chỉ dùng một lần cùng với yêu cầu đăng nhập. Apple sẽ chuyển hàm băm này mà không thay đổi trong phản hồi. Firebase xác thực phản hồi bằng cách băm số chỉ dùng một lần ban đầu và so sánh số đó với giá trị do Apple truyền.
Bắt đầu quy trình đăng nhập của Apple bằng thư viện OAuth hoặc phương thức khác. Nhớ thêm số chỉ dùng một lần đã băm làm tham số trong yêu cầu của bạn.
Sau khi bạn nhận được phản hồi của Apple, hãy lấy mã thông báo nhận dạng từ phản hồi đó và sử dụng mã thông báo đó cùng với số chỉ dùng một lần chưa băm để tạo
AuthCredential
:Kotlin
val credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build()
Java
AuthCredential credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build();
Xác thực bằng Firebase bằng thông tin đăng nhập Firebase:
Kotlin
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. // ... } } });
Nếu lệnh gọi đến signInWithCredential
thành công, bạn có thể dùng phương thức getCurrentUser
để lấy dữ liệu tài khoản của người dùng.
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
Ngoài ra, những ứng dụng hỗ trợ tính năng Đăng nhập bằng Apple nên sử dụng REST API Đăng nhập bằng Apple để thu hồi mã thông báo người dùng.
Để đáp ứng yêu cầu này, hãy triển khai các bước sau:
Sử dụng phương thức
startActivityForSignInWithProvider()
để đăng nhập bằng Apple và nhậnAuthResult
.Lấy mã thông báo truy cập cho nhà cung cấp Apple.
Kotlin
val oauthCredential: OAuthCredential = authResult.credential val accessToken = oauthCredential.accessToken
Java
OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential(); String accessToken = oauthCredential.getAccessToken();
Thu hồi mã thông báo bằng API
revokeAccessToken
.Kotlin
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 ... } } });
- 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)
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 mình, bạn có thể lấy thông tin cơ bản về hồ sơ của người dùng từ đối tượng
FirebaseUser
. Hãy 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
:Kotlin
Firebase.auth.signOut()
Java
FirebaseAuth.getInstance().signOut();
-