Firebase Authentication सेशन लंबे समय तक चलते हैं. जब भी कोई उपयोगकर्ता साइन इन करता है, तो उपयोगकर्ता के क्रेडेंशियल Firebase Authentication बैकएंड पर भेजे जाते हैं. इसके बाद, उन्हें Firebase आईडी टोकन (JWT) और रीफ़्रेश टोकन के लिए बदल दिया जाता है. Firebase आईडी टोकन का इस्तेमाल एक घंटे के लिए किया जा सकता है. रिफ़्रेश टोकन का इस्तेमाल, नए आईडी टोकन पाने के लिए किया जा सकता है. रीफ़्रेश टोकन की समयसीमा सिर्फ़ तब खत्म होती है, जब इनमें से कोई एक स्थिति होती है:
- उपयोगकर्ता का खाता मिटा दिया गया है
- उपयोगकर्ता का खाता बंद है
- उपयोगकर्ता के खाते में कोई बड़ा बदलाव हुआ हो. इसमें पासवर्ड या ईमेल पते के अपडेट जैसे इवेंट शामिल हैं.
Firebase Admin SDK टूल की मदद से, किसी खास उपयोगकर्ता के लिए रीफ़्रेश टोकन रद्द किए जा सकते हैं. इसके अलावा, आईडी टोकन रद्द होने की जांच करने के लिए एक एपीआई भी उपलब्ध कराया गया है. इन सुविधाओं की मदद से, आपके पास उपयोगकर्ता के सेशन पर ज़्यादा कंट्रोल होता है. SDK टूल की मदद से, सेशन के इस्तेमाल पर पाबंदियां लगाई जा सकती हैं, ताकि संदिग्ध परिस्थितियों में उनका इस्तेमाल न किया जा सके. साथ ही, टोकन की चोरी होने पर उसे वापस पाने का तरीका भी उपलब्ध कराया जा सकता है.
रीफ़्रेश टोकन रद्द करना
जब कोई उपयोगकर्ता खोए या चोरी हुए डिवाइस की शिकायत करता है, तो आपके पास उसके मौजूदा रीफ़्रेश टोकन को रद्द करने का विकल्प होता है. इसी तरह, अगर आपको कोई सामान्य जोखिम दिखता है या आपको लगता है कि ऐक्टिव टोकन बड़े पैमाने पर लीक हो गए हैं, तो सभी उपयोगकर्ताओं को खोजने और किसी प्रोजेक्ट के लिए उनके टोकन रद्द करने के लिए, listUsers
एपीआई का इस्तेमाल किया जा सकता है.
पासवर्ड रीसेट करने पर, उपयोगकर्ता के मौजूदा टोकन भी रद्द हो जाते हैं. हालांकि, इस मामले में Firebase Authentication बैकएंड, टोकन रद्द करने की प्रोसेस को अपने-आप मैनेज करता है. अनुमति रद्द करने पर, उपयोगकर्ता को साइन आउट कर दिया जाता है और फिर से पुष्टि करने के लिए कहा जाता है.
यहां एक उदाहरण दिया गया है, जिसमें किसी उपयोगकर्ता के रीफ़्रेश टोकन को रद्द करने के लिए, Admin SDK का इस्तेमाल किया गया है. एडमिन SDK टूल को शुरू करने के लिए, सेटअप पेज पर दिए गए निर्देशों का पालन करें.
Node.js
// Revoke all refresh tokens for a specified user for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
getAuth()
.revokeRefreshTokens(uid)
.then(() => {
return getAuth().getUser(uid);
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
})
.then((timestamp) => {
console.log(`Tokens revoked at: ${timestamp}`);
});
Java
FirebaseAuth.getInstance().revokeRefreshTokens(uid);
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
// Convert to seconds as the auth_time in the token claims is in seconds too.
long revocationSecond = user.getTokensValidAfterTimestamp() / 1000;
System.out.println("Tokens revoked at: " + revocationSecond);
Python
# Revoke tokens on the backend.
auth.revoke_refresh_tokens(uid)
user = auth.get_user(uid)
# Convert to seconds as the auth_time in the token claims is in seconds.
revocation_second = user.tokens_valid_after_timestamp / 1000
print('Tokens revoked at: {0}'.format(revocation_second))
शुरू करें
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
if err := client.RevokeRefreshTokens(ctx, uid); err != nil {
log.Fatalf("error revoking tokens for user: %v, %v\n", uid, err)
}
// accessing the user's TokenValidAfter
u, err := client.GetUser(ctx, uid)
if err != nil {
log.Fatalf("error getting user %s: %v\n", uid, err)
}
timestamp := u.TokensValidAfterMillis / 1000
log.Printf("the refresh tokens were revoked at: %d (UTC seconds) ", timestamp)
C#
await FirebaseAuth.DefaultInstance.RevokeRefreshTokensAsync(uid);
var user = await FirebaseAuth.DefaultInstance.GetUserAsync(uid);
Console.WriteLine("Tokens revoked at: " + user.TokensValidAfterTimestamp);
आईडी टोकन के निरस्त होने का पता लगाना
Firebase आईडी टोकन, स्टेटलेस JWT होते हैं. इसलिए, Firebase Authentication बैकएंड से टोकन की स्थिति का अनुरोध करके ही यह पता लगाया जा सकता है कि टोकन रद्द कर दिया गया है या नहीं. इस वजह से, अपने सर्वर पर यह जांच करना महंगा होता है. इसके लिए, नेटवर्क के एक और राउंड ट्रिप की ज़रूरत होती है. इस नेटवर्क अनुरोध को करने से बचा जा सकता है. इसके लिए, Firebase Security Rules को सेट अप करें. यह पुष्टि करने के लिए, एडमिन SDK टूल का इस्तेमाल करने के बजाय, रद्द करने की जांच करता है.
Firebase Security Rules में आईडी टोकन के निरस्त होने का पता लगाना
सुरक्षा नियमों का इस्तेमाल करके, आईडी टोकन के रद्द होने का पता लगाने के लिए, हमें पहले उपयोगकर्ता के हिसाब से कुछ मेटाडेटा सेव करना होगा.
Firebase Realtime Database में, उपयोगकर्ता के हिसाब से मेटाडेटा अपडेट करें.
रीफ़्रेश टोकन रद्द करने का टाइमस्टैंप सेव करें. Firebase Security Rules की मदद से, आईडी टोकन को वापस लेने की प्रोसेस को ट्रैक करने के लिए, यह ज़रूरी है. इससे डेटाबेस में बेहतर तरीके से जांच की जा सकती है. नीचे दिए गए कोड सैंपल में, पिछले सेक्शन में मिले uid और रद्द करने के समय का इस्तेमाल करें.
Node.js
const metadataRef = getDatabase().ref('metadata/' + uid);
metadataRef.set({ revokeTime: utcRevocationTimeSecs }).then(() => {
console.log('Database updated successfully.');
});
Java
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("metadata/" + uid);
Map<String, Object> userData = new HashMap<>();
userData.put("revokeTime", revocationSecond);
ref.setValueAsync(userData);
Python
metadata_ref = firebase_admin.db.reference("metadata/" + uid)
metadata_ref.set({'revokeTime': revocation_second})
Firebase Security Rules पर सही का निशान लगाएं
इस जांच को लागू करने के लिए, हर उपयोगकर्ता के लिए रद्द करने का समय सेव करने के लिए, क्लाइंट के लिखने के ऐक्सेस के बिना कोई नियम सेट अप करें. इसे, पिछले उदाहरणों में दिखाए गए तरीके से, अनुमति रद्द करने के आखिरी समय के यूटीसी टाइमस्टैंप के साथ अपडेट किया जा सकता है:
{
"rules": {
"metadata": {
"$user_id": {
// this could be false as it is only accessed from backend or rules.
".read": "$user_id === auth.uid",
".write": "false",
}
}
}
}
जिस डेटा को पुष्टि किए गए ऐक्सेस की ज़रूरत है उसके लिए, यह नियम कॉन्फ़िगर होना चाहिए. इस लॉजिक के तहत, सिर्फ़ पुष्टि किए गए ऐसे उपयोगकर्ता सुरक्षित डेटा ऐक्सेस कर सकते हैं जिनके आईडी टोकन रद्द नहीं किए गए हैं:
{
"rules": {
"users": {
"$user_id": {
".read": "auth != null && $user_id === auth.uid && (
!root.child('metadata').child(auth.uid).child('revokeTime').exists()
|| auth.token.auth_time > root.child('metadata').child(auth.uid).child('revokeTime').val()
)",
".write": "auth != null && $user_id === auth.uid && (
!root.child('metadata').child(auth.uid).child('revokeTime').exists()
|| auth.token.auth_time > root.child('metadata').child(auth.uid).child('revokeTime').val()
)",
}
}
}
}
SDK टूल में आईडी टोकन रद्द होने का पता लगाएं.
अपने सर्वर में, रीफ़्रेश टोकन रद्द करने और आईडी टोकन की पुष्टि करने के लिए, यह लॉजिक लागू करें:
जब किसी उपयोगकर्ता के आईडी टोकन की पुष्टि करनी हो, तो checkRevoked
बोलियन फ़्लैग को verifyIdToken
पर पास करना होगा. अगर उपयोगकर्ता का टोकन रद्द कर दिया जाता है, तो उपयोगकर्ता को क्लाइंट से साइन आउट कर दिया जाना चाहिए या Firebase Authentication क्लाइंट एसडीके से मिले, फिर से पुष्टि करने वाले एपीआई का इस्तेमाल करके, फिर से पुष्टि करने के लिए कहा जाना चाहिए.
अपने प्लैटफ़ॉर्म के लिए Admin SDK टूल को शुरू करने के लिए, सेटअप पेज पर दिए गए निर्देशों का पालन करें. आईडी टोकन को वापस पाने के उदाहरण, verifyIdToken
सेक्शन में दिए गए हैं.
Node.js
// Verify the ID token while checking if the token is revoked by passing
// checkRevoked true.
let checkRevoked = true;
getAuth()
.verifyIdToken(idToken, checkRevoked)
.then((payload) => {
// Token is valid.
})
.catch((error) => {
if (error.code == 'auth/id-token-revoked') {
// Token has been revoked. Inform the user to reauthenticate or signOut() the user.
} else {
// Token is invalid.
}
});
Java
try {
// Verify the ID token while checking if the token is revoked by passing checkRevoked
// as true.
boolean checkRevoked = true;
FirebaseToken decodedToken = FirebaseAuth.getInstance()
.verifyIdToken(idToken, checkRevoked);
// Token is valid and not revoked.
String uid = decodedToken.getUid();
} catch (FirebaseAuthException e) {
if (e.getAuthErrorCode() == AuthErrorCode.REVOKED_ID_TOKEN) {
// Token has been revoked. Inform the user to re-authenticate or signOut() the user.
} else {
// Token is invalid.
}
}
Python
try:
# Verify the ID token while checking if the token is revoked by
# passing check_revoked=True.
decoded_token = auth.verify_id_token(id_token, check_revoked=True)
# Token is valid and not revoked.
uid = decoded_token['uid']
except auth.RevokedIdTokenError:
# Token revoked, inform the user to reauthenticate or signOut().
pass
except auth.UserDisabledError:
# Token belongs to a disabled user record.
pass
except auth.InvalidIdTokenError:
# Token is invalid
pass
शुरू करें
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
token, err := client.VerifyIDTokenAndCheckRevoked(ctx, idToken)
if err != nil {
if err.Error() == "ID token has been revoked" {
// Token is revoked. Inform the user to reauthenticate or signOut() the user.
} else {
// Token is invalid
}
}
log.Printf("Verified ID token: %v\n", token)
C#
try
{
// Verify the ID token while checking if the token is revoked by passing checkRevoked
// as true.
bool checkRevoked = true;
var decodedToken = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(
idToken, checkRevoked);
// Token is valid and not revoked.
string uid = decodedToken.Uid;
}
catch (FirebaseAuthException ex)
{
if (ex.AuthErrorCode == AuthErrorCode.RevokedIdToken)
{
// Token has been revoked. Inform the user to re-authenticate or signOut() the user.
}
else
{
// Token is invalid.
}
}
क्लाइंट पर, टोकन रद्द करने का जवाब देना
अगर Admin SDK की मदद से टोकन रद्द किया जाता है, तो क्लाइंट को इसकी जानकारी दी जाती है. साथ ही, उपयोगकर्ता को फिर से पुष्टि करने या साइन आउट करने के लिए कहा जाता है:
function onIdTokenRevocation() {
// For an email/password user. Prompt the user for the password again.
let password = prompt('Please provide your password for reauthentication');
let credential = firebase.auth.EmailAuthProvider.credential(
firebase.auth().currentUser.email, password);
firebase.auth().currentUser.reauthenticateWithCredential(credential)
.then(result => {
// User successfully reauthenticated. New ID tokens should be valid.
})
.catch(error => {
// An error occurred.
});
}
बेहतर सुरक्षा: आईपी पते से जुड़ी पाबंदियां लागू करना
टोकन की चोरी का पता लगाने के लिए, आम तौर पर अनुरोध के आईपी पते के ऑरिजिन को ट्रैक किया जाता है. उदाहरण के लिए, अगर अनुरोध हमेशा एक ही आईपी पते (कॉल करने वाले सर्वर) से आ रहे हैं, तो एक ही आईपी पते के सेशन लागू किए जा सकते हैं. इसके अलावा, अगर आपको पता चलता है कि उपयोगकर्ता के आईपी पते की जगह की जानकारी अचानक बदल गई है या आपको किसी संदिग्ध सोर्स से अनुरोध मिलता है, तो उपयोगकर्ता के टोकन को रद्द किया जा सकता है.
आईपी पते के आधार पर सुरक्षा जांच करने के लिए, पुष्टि किए गए हर अनुरोध के लिए आईडी टोकन की जांच करें. साथ ही, यह भी देखें कि अनुरोध का आईपी पता, पहले से भरोसेमंद आईपी पतों से मेल खाता है या भरोसेमंद रेंज में है. इसके बाद ही, पाबंदी वाले डेटा का ऐक्सेस दें. उदाहरण के लिए:
app.post('/getRestrictedData', (req, res) => {
// Get the ID token passed.
const idToken = req.body.idToken;
// Verify the ID token, check if revoked and decode its payload.
admin.auth().verifyIdToken(idToken, true).then((claims) => {
// Get the user's previous IP addresses, previously saved.
return getPreviousUserIpAddresses(claims.sub);
}).then(previousIpAddresses => {
// Get the request IP address.
const requestIpAddress = req.connection.remoteAddress;
// Check if the request IP address origin is suspicious relative to previous
// IP addresses. The current request timestamp and the auth_time of the ID
// token can provide additional signals of abuse especially if the IP address
// suddenly changed. If there was a sudden location change in a
// short period of time, then it will give stronger signals of possible abuse.
if (!isValidIpAddress(previousIpAddresses, requestIpAddress)) {
// Invalid IP address, take action quickly and revoke all user's refresh tokens.
revokeUserTokens(claims.uid).then(() => {
res.status(401).send({error: 'Unauthorized access. Please login again!'});
}, error => {
res.status(401).send({error: 'Unauthorized access. Please login again!'});
});
} else {
// Access is valid. Try to return data.
getData(claims).then(data => {
res.end(JSON.stringify(data);
}, error => {
res.status(500).send({ error: 'Server error!' })
});
}
});
});