Firebase SDK टूल का इस्तेमाल करके, उपयोगकर्ताओं को अपने Apple आईडी का इस्तेमाल करके Firebase से पुष्टि करने की अनुमति दी जा सकती है. इसके लिए, OAuth 2.0 साइन-इन फ़्लो को एंड-टू-एंड पूरा करना होगा.
शुरू करने से पहले
Apple का इस्तेमाल करके उपयोगकर्ताओं को साइन इन करने के लिए, पहले Apple की डेवलपर साइट पर 'Apple से साइन इन करें' सुविधा को कॉन्फ़िगर करें. इसके बाद, अपने Firebase प्रोजेक्ट के लिए, Apple को साइन-इन करने की सुविधा देने वाले प्रोवाइडर के तौर पर चालू करें.
Apple Developer Program में शामिल होना
Apple Developer Program के सदस्य ही, 'Apple से साइन इन करें' सुविधा को कॉन्फ़िगर कर सकते हैं.
'Apple से साइन इन करें' सुविधा को कॉन्फ़िगर करना
Apple Developer साइट पर, ये काम करें:
-
अपनी वेबसाइट को अपने ऐप्लिकेशन से जोड़ें. इसके लिए, वेब के लिए 'Apple से साइन इन करें' सुविधा को कॉन्फ़िगर करने के पहले सेक्शन में दिया गया तरीका अपनाएं. जब कहा जाए, तब इस यूआरएल को रिटर्न यूआरएल के तौर पर रजिस्टर करें:
https://
YOUR_FIREBASE_PROJECT_ID .firebaseapp.com/__/auth/handlerFirebase प्रोजेक्ट आईडी पाने के लिए, Firebase कंसोल के सेटिंग पेज पर जाएं.
इसके बाद, अपने नए सेवा आईडी को नोट कर लें. आपको अगले सेक्शन में इसकी ज़रूरत पड़ेगी.
- 'Apple से साइन इन करें' की निजी पासकोड बनाएं. अगले सेक्शन में, आपको अपनी नई निजी कुंजी और कुंजी आईडी की ज़रूरत पड़ेगी.
-
अगर Firebase Authentication की किसी ऐसी सुविधा का इस्तेमाल किया जाता है जिससे उपयोगकर्ताओं को ईमेल भेजे जाते हैं, तो Apple की निजी ईमेल रिले सेवा को कॉन्फ़िगर करें और
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com
(या पसंद के मुताबिक बनाए गए ईमेल टेंप्लेट का डोमेन) रजिस्टर करें. इससे Apple, Firebase Authentication से भेजे गए ईमेल को, गुप्त रखे गए Apple ईमेल पतों पर भेज सकता है.
साइन इन करने की सुविधा देने वाली सेवा के तौर पर Apple को चालू करना
- अपने Android प्रोजेक्ट में Firebase जोड़ें. Firebase कंसोल में ऐप्लिकेशन सेट अप करते समय, अपने ऐप्लिकेशन का SHA-1 हस्ताक्षर रजिस्टर करना न भूलें.
- Firebase कंसोल में, Auth सेक्शन खोलें. साइन इन करने का तरीका टैब पर, Apple की सेवा देने वाली कंपनी को चालू करें. पिछले सेक्शन में बनाया गया सेवा आईडी डालें. साथ ही, OAuth कोड फ़्लो कॉन्फ़िगरेशन सेक्शन में, अपना Apple टीम आईडी और पिछले सेक्शन में बनाई गई निजी कुंजी और कुंजी आईडी डालें.
पहचान छिपाकर रखे गए डेटा से जुड़ी Apple की ज़रूरी शर्तों का पालन करना
'Apple से साइन इन करें' सुविधा की मदद से, उपयोगकर्ता अपने डेटा को छिपाने का विकल्प चुन सकते हैं. इसमें, उनके ईमेल पते का डेटा भी शामिल है. इस विकल्प को चुनने वाले उपयोगकर्ताओं के पास privaterelay.appleid.com
डोमेन वाले ईमेल पते होते हैं. अपने ऐप्लिकेशन में 'Apple से साइन इन करें' सुविधा का इस्तेमाल करते समय, आपको इन गुप्त Apple आईडी के लिए, डेवलपर के लिए बनी नीतियों या Apple की शर्तों का पालन करना होगा.
इसमें, उपयोगकर्ता की पहचान ज़ाहिर करने वाली किसी भी जानकारी को, बिना पहचान वाले Apple आईडी से जोड़ने से पहले, उपयोगकर्ता की सहमति लेना भी शामिल है. Firebase Authentication का इस्तेमाल करते समय, ये कार्रवाइयां की जा सकती हैं:
- किसी ईमेल पते को छिपाए गए Apple आईडी से लिंक करें या इसके उलट.
- किसी फ़ोन नंबर को छिपाए गए नाम वाले Apple आईडी से लिंक करना या इसके उलट
- किसी ऐसे सोशल क्रेडेंशियल (Facebook, Google वगैरह) को, पहचान छिपाकर इस्तेमाल किए जा रहे Apple आईडी से लिंक करना या इसके उलट.
ऊपर दी गई सूची में प्रॉडक्ट और सेवाओं के सिर्फ़ कुछ उदाहरण दिए गए हैं. अपने डेवलपर खाते के सदस्यता सेक्शन में, Apple Developer Program के लाइसेंस एग्रीमेंट को पढ़ें. इससे आपको यह पक्का करने में मदद मिलेगी कि आपका ऐप्लिकेशन, Apple की ज़रूरी शर्तों को पूरा करता है या नहीं.
Firebase SDK टूल की मदद से, साइन इन फ़्लो को मैनेज करना
Android पर, Firebase की मदद से उपयोगकर्ताओं की पुष्टि करने का सबसे आसान तरीका यह है कि उनके Apple खातों का इस्तेमाल करके, Firebase Android SDK टूल की मदद से पूरे साइन-इन फ़्लो को मैनेज किया जाए.
Firebase Android SDK टूल की मदद से साइन-इन फ़्लो को मैनेज करने के लिए, यह तरीका अपनाएं:
बिल्डर का इस्तेमाल करके
OAuthProvider
का एक इंस्टेंस बनाएं. इसके लिए, प्रोवाइडर आईडीapple.com
का इस्तेमाल करें:val provider = OAuthProvider.newBuilder("apple.com")
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
ज़रूरी नहीं: डिफ़ॉल्ट दायरों के अलावा, OAuth 2.0 के अन्य दायरे भी तय करें. इन दायरों के लिए, आपको पुष्टि करने वाली कंपनी से अनुरोध करना है.
provider.setScopes(arrayOf("email", "name"))
List<String> scopes = new ArrayList<String>() { { add("email"); add("name"); } }; provider.setScopes(scopes);
हर ईमेल पते के लिए एक खाता चालू होने पर, Firebase डिफ़ॉल्ट रूप से ईमेल और नाम के स्कोप का अनुरोध करता है. अगर इस सेटिंग को हर ईमेल पते के लिए एक से ज़्यादा खाते पर सेट किया जाता है, तो Firebase तब तक Apple से किसी स्कोप का अनुरोध नहीं करता, जब तक आपने उन्हें नहीं बताया.
ज़रूरी नहीं: अगर आपको Apple की साइन-इन स्क्रीन को अंग्रेज़ी के अलावा किसी दूसरी भाषा में दिखाना है, तो
locale
पैरामीटर सेट करें. जिन भाषाओं में यह सुविधा काम करती है उनके लिए, 'Apple से साइन इन करें' दस्तावेज़ देखें.// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr")
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr");
OAuth प्रोवाइडर ऑब्जेक्ट का इस्तेमाल करके, Firebase से पुष्टि करना. ध्यान दें कि
FirebaseAuth
के अन्य ऑपरेशन के उलट, यह आपके यूज़र इंटरफ़ेस (यूआई) को कंट्रोल करेगा. इसके लिए, यह कस्टम Chrome टैब खोलेगा. इसलिए, अटैच किए गएOnSuccessListener
औरOnFailureListener
में अपनी गतिविधि का रेफ़रंस न दें, क्योंकि ऑपरेशन के यूज़र इंटरफ़ेस (यूआई) के शुरू होने पर, वे तुरंत अलग हो जाएंगे.आपको सबसे पहले यह देखना चाहिए कि क्या आपको पहले से ही जवाब मिल चुका है. इस तरीके से साइन इन करने पर, आपकी गतिविधि बैकग्राउंड में चलती है. इसका मतलब है कि साइन इन फ़्लो के दौरान, सिस्टम इसे फिर से ऐक्सेस कर सकता है. अगर ऐसा होता है, तो उपयोगकर्ता को फिर से कोशिश करने के लिए न कहें. इसके लिए, यह देखें कि नतीजा पहले से मौजूद है या नहीं.
यह देखने के लिए कि कोई नतीजा बाकी है या नहीं,
getPendingAuthResult()
पर कॉल करें: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") }
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"); }
अगर कोई नतीजा बाकी नहीं है, तो साइन इन फ़्लो शुरू करने के लिए,
startActivityForSignInWithProvider()
को कॉल करें: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) }
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); } });
Firebase Auth के साथ काम करने वाली अन्य सेवाओं के उलट, Apple फ़ोटो का यूआरएल नहीं देता.
साथ ही, जब कोई उपयोगकर्ता ऐप्लिकेशन के साथ अपना ईमेल पता शेयर नहीं करता है, तो Apple उस उपयोगकर्ता के लिए एक यूनीक ईमेल पता (
xyz@privaterelay.appleid.com
फ़ॉर्म में) उपलब्ध कराता है. इस पते को आपके ऐप्लिकेशन के साथ शेयर किया जाता है. अगर आपने निजी ईमेल रिले सेवा को कॉन्फ़िगर किया है, तो Apple, उपयोगकर्ता के असली ईमेल पते पर, गुप्त पते पर भेजे गए ईमेल फ़ॉरवर्ड करता है.Apple, उपयोगकर्ता की जानकारी सिर्फ़ ऐप्लिकेशन के साथ शेयर करता है. जैसे, डिसप्ले नेम. ऐसा तब होता है, जब उपयोगकर्ता पहली बार साइन इन करता है. आम तौर पर, Firebase डिसप्ले नेम को तब सेव करता है, जब कोई उपयोगकर्ता पहली बार Apple से साइन इन करता है. यह जानकारी आपको
getCurrentUser().getDisplayName()
से मिल सकती है. हालांकि, अगर आपने पहले Firebase का इस्तेमाल किए बिना, उपयोगकर्ता को ऐप्लिकेशन में साइन इन करने के लिए Apple का इस्तेमाल किया था, तो Apple, Firebase को उपयोगकर्ता का डिसप्ले नेम नहीं देगा.
फिर से पुष्टि करना और खाता लिंक करना
इस पैटर्न का इस्तेमाल startActivityForReauthenticateWithProvider()
के साथ किया जा सकता है. इसका इस्तेमाल, संवेदनशील कार्रवाइयों के लिए नया क्रेडेंशियल पाने के लिए किया जा सकता है. इन कार्रवाइयों के लिए, हाल ही में साइन इन करना ज़रूरी होता है:
// 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.
})
// 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.
}
});
साथ ही, linkWithCredential()
का इस्तेमाल करके, अलग-अलग पहचान की पुष्टि करने वाली सेवाओं को मौजूदा खातों से लिंक किया जा सकता है.
ध्यान दें कि उपयोगकर्ताओं के Apple खातों को अन्य डेटा से लिंक करने से पहले, आपको उनकी साफ़ तौर पर सहमति लेनी होगी.
उदाहरण के लिए, किसी Facebook खाते को मौजूदा Firebase खाते से लिंक करने के लिए, उस ऐक्सेस टोकन का इस्तेमाल करें जो आपको Facebook में साइन इन करने पर मिला था:
// 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.
}
});
// 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.
}
}
});
बेहतर सुविधा: साइन इन फ़्लो को मैन्युअल तरीके से मैनेज करना
Apple खाते का इस्तेमाल करके भी Firebase की पुष्टि की जा सकती है. इसके लिए, साइन-इन फ़्लो को मैनेज करें. इसके लिए, Apple Sign-In JS SDK टूल का इस्तेमाल करें, मैन्युअल तरीके से OAuth फ़्लो बनाएं या AppAuth जैसी OAuth लाइब्रेरी का इस्तेमाल करें.
हर साइन-इन अनुरोध के लिए, एक रैंडम स्ट्रिंग जनरेट करें—"नॉन्स"—जिसका इस्तेमाल करके यह पक्का किया जा सकेगा कि आपको जो आईडी टोकन मिला है वह खास तौर पर आपके ऐप्लिकेशन के पुष्टि करने के अनुरोध के जवाब में दिया गया है. रीप्ले अटैक से बचने के लिए, यह चरण ज़रूरी है.
Android पर
SecureRandom
की मदद से, क्रिप्टोग्राफ़िक तरीके से सुरक्षित नॉन्स जनरेट किया जा सकता है. इसका उदाहरण यहां दिया गया है: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() }
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(); }
इसके बाद, हेक्स स्ट्रिंग के तौर पर नॉन्स का SHA246 हैश पाएं:
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() }
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(); }
आपको साइन इन करने के अनुरोध के साथ, नॉन्स का SHA256 हैश भेजना होगा. Apple, रिस्पॉन्स में इसे बिना किसी बदलाव के पास कर देगा. Firebase, ओरिजनल नॉन्स को हैश करके और उसकी तुलना Apple की दी गई वैल्यू से करके, जवाब की पुष्टि करता है.
अपनी OAuth लाइब्रेरी या किसी अन्य तरीके का इस्तेमाल करके, Apple के साइन-इन फ़्लो को शुरू करें. अपने अनुरोध में, हैश किए गए नॉन्स को पैरामीटर के तौर पर शामिल करना न भूलें.
Apple से जवाब मिलने के बाद, जवाब से आईडी टोकन पाएं और
AuthCredential
बनाने के लिए, उसका और अनहैश किए गए नॉन्स का इस्तेमाल करें:val credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build()
AuthCredential credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build();
Firebase क्रेडेंशियल का इस्तेमाल करके, Firebase से पुष्टि करना:
auth.signInWithCredential(credential) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // User successfully signed in with Apple ID token. // ... } }
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. // ... } } });
अगर signInWithCredential
को कॉल करने पर कोई गड़बड़ी नहीं मिलती है, तो उपयोगकर्ता के खाते का डेटा पाने के लिए, getCurrentUser
के तरीके का इस्तेमाल किया जा सकता है.
टोकन रद्द करना
Apple की ज़रूरी शर्त के मुताबिक, खाता बनाने की सुविधा देने वाले ऐप्लिकेशन में उपयोगकर्ताओं को, अपने खाते को मिटाने का अनुरोध करने की सुविधा देनी चाहिए. इस बारे में App Store की समीक्षा से जुड़े दिशा-निर्देशों में बताया गया है
इसके अलावा, 'Apple से साइन इन करें' सुविधा के साथ काम करने वाले ऐप्लिकेशन को उपयोगकर्ता के टोकन रद्द करने के लिए, 'Apple से साइन इन करें' REST API का इस्तेमाल करना चाहिए.
इस ज़रूरी शर्त को पूरा करने के लिए, यह तरीका अपनाएं:
Apple का इस्तेमाल करके साइन-इन करने के लिए,
startActivityForSignInWithProvider()
का तरीका अपनाएं औरAuthResult
पाएं.Apple की सेवा देने वाली कंपनी के लिए ऐक्सेस टोकन पाएं.
val oauthCredential: OAuthCredential = authResult.credential val accessToken = oauthCredential.accessToken
OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential(); String accessToken = oauthCredential.getAccessToken();
revokeAccessToken
API का इस्तेमाल करके, टोकन रद्द करें.mAuth.revokeAccessToken(accessToken) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Access token successfully revoked // for the user ... } }
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 ... } } });
- आखिर में, उपयोगकर्ता खाता और उससे जुड़ा सारा डेटा मिटाएं
अगले चरण
जब कोई उपयोगकर्ता पहली बार साइन इन करता है, तो एक नया उपयोगकर्ता खाता बन जाता है. साथ ही, उसे उन क्रेडेंशियल से लिंक कर दिया जाता है जिनका इस्तेमाल करके उपयोगकर्ता ने साइन इन किया था. जैसे, उपयोगकर्ता का नाम और पासवर्ड, फ़ोन नंबर या पुष्टि करने वाली सेवा की जानकारी. इस नए खाते को आपके Firebase प्रोजेक्ट के हिस्से के तौर पर सेव किया जाता है. इसका इस्तेमाल, आपके प्रोजेक्ट के हर ऐप्लिकेशन में किसी उपयोगकर्ता की पहचान करने के लिए किया जा सकता है. भले ही, उपयोगकर्ता साइन इन करने का कोई भी तरीका अपनाए.
-
अपने ऐप्लिकेशन में, उपयोगकर्ता की प्रोफ़ाइल की बुनियादी जानकारी पाने के लिए,
FirebaseUser
ऑब्जेक्ट का इस्तेमाल किया जा सकता है. उपयोगकर्ताओं को मैनेज करें देखें. अपने Firebase Realtime Database और Cloud Storage सुरक्षा नियमों में, आपके पास
auth
वैरिएबल से साइन इन किए गए उपयोगकर्ता का यूनीक उपयोगकर्ता आईडी पाने का विकल्प है. साथ ही, इसका इस्तेमाल करके यह कंट्रोल किया जा सकता है कि उपयोगकर्ता कौनसा डेटा ऐक्सेस कर सकता है.
पुष्टि करने वाली सेवा देने वाली कंपनी के क्रेडेंशियल को किसी मौजूदा उपयोगकर्ता खाते से लिंक करके, उपयोगकर्ताओं को पुष्टि करने वाली कई कंपनियों का इस्तेमाल करके, आपके ऐप्लिकेशन में साइन इन करने की अनुमति दी जा सकती है.
किसी उपयोगकर्ता को साइन आउट करने के लिए,
signOut
को कॉल करें:Firebase.auth.signOut()
FirebaseAuth.getInstance().signOut();
-