ব্যবহারকারীর সেশন পরিচালনা করুন

Firebase Authentication সেশন দীর্ঘজীবী হয়। Every time a user signs in, the user credentials are sent to the Firebase Authentication backend and exchanged for a Firebase ID token (a JWT) and refresh token. ফায়ারবেস আইডি টোকেনগুলি স্বল্পস্থায়ী এবং এক ঘন্টার জন্য স্থায়ী; নতুন আইডি টোকেন পুনরুদ্ধার করতে রিফ্রেশ টোকেন ব্যবহার করা যেতে পারে। রিফ্রেশ টোকেনগুলি কেবল তখনই শেষ হয় যখন নিম্নলিখিতগুলির মধ্যে একটি ঘটে:

  • ব্যবহারকারী মুছে ফেলা হয়
  • ব্যবহারকারী অক্ষম
  • ব্যবহারকারীর জন্য একটি বড় অ্যাকাউন্ট পরিবর্তন সনাক্ত করা হয়। এর মধ্যে রয়েছে পাসওয়ার্ড বা ইমেল ঠিকানা আপডেটের মতো ঘটনা।

Firebase অ্যাডমিন SDK একটি নির্দিষ্ট ব্যবহারকারীর জন্য রিফ্রেশ টোকেন প্রত্যাহার করার ক্ষমতা প্রদান করে। এছাড়াও, আইডি টোকেন প্রত্যাহার চেক করার জন্য একটি এপিআইও উপলব্ধ করা হয়েছে। এই ক্ষমতাগুলির সাথে, ব্যবহারকারীর সেশনগুলির উপর আপনার আরও নিয়ন্ত্রণ রয়েছে। The SDK provides the ability to add restrictions to prevent sessions from being used in suspicious circumstances, as well as a mechanism for recovery from potential token theft.

রিফ্রেশ টোকেন প্রত্যাহার করুন

আপনি একজন ব্যবহারকারীর বিদ্যমান রিফ্রেশ টোকেন প্রত্যাহার করতে পারেন যখন একজন ব্যবহারকারী একটি হারিয়ে যাওয়া বা চুরি হওয়া ডিভাইসের রিপোর্ট করে। Similarly, if you discover a general vulnerability or suspect a wide-scale leak of active tokens, you can use the listUsers API to look up all users and revoke their tokens for the specified project.

পাসওয়ার্ড রিসেট ব্যবহারকারীর বিদ্যমান টোকেন প্রত্যাহার করে; যাইহোক, Firebase Authentication ব্যাকএন্ড সেই ক্ষেত্রে স্বয়ংক্রিয়ভাবে প্রত্যাহার পরিচালনা করে। প্রত্যাহারে, ব্যবহারকারী সাইন আউট হয় এবং পুনরায় প্রমাণীকরণের জন্য অনুরোধ করা হয়।

এখানে একটি উদাহরণ বাস্তবায়ন যা প্রদত্ত ব্যবহারকারীর রিফ্রেশ টোকেন প্রত্যাহার করতে অ্যাডমিন 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}`);
  });

জাভা

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

পাইথন

# 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)

সি#

await FirebaseAuth.DefaultInstance.RevokeRefreshTokensAsync(uid);
var user = await FirebaseAuth.DefaultInstance.GetUserAsync(uid);
Console.WriteLine("Tokens revoked at: " + user.TokensValidAfterTimestamp);

আইডি টোকেন প্রত্যাহার সনাক্ত করুন

Because Firebase ID tokens are stateless JWTs, you can determine a token has been revoked only by requesting the token's status from the Firebase Authentication backend. এই কারণে, আপনার সার্ভারে এই চেকটি সম্পাদন করা একটি ব্যয়বহুল অপারেশন, অতিরিক্ত নেটওয়ার্ক রাউন্ড ট্রিপের প্রয়োজন। You can avoid making this network request by setting up Firebase Security Rules that check for revocation rather than using the Admin SDK to make the check.

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

জাভা

DatabaseReference ref = FirebaseDatabase.getInstance().getReference("metadata/" + uid);
Map<String, Object> userData = new HashMap<>();
userData.put("revokeTime", revocationSecond);
ref.setValueAsync(userData);

পাইথন

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()
        )",
      }
    }
  }
}

এসডিকে আইডি টোকেন প্রত্যাহার সনাক্ত করুন।

আপনার সার্ভারে, রিফ্রেশ টোকেন প্রত্যাহার এবং আইডি টোকেন বৈধতার জন্য নিম্নলিখিত যুক্তি প্রয়োগ করুন:

যখন একজন ব্যবহারকারীর আইডি টোকেন যাচাই করতে হবে, তখন অতিরিক্ত checkRevoked বুলিয়ান পতাকাটি verifyIdToken করতে হবে। If the user's token is revoked, the user should be signed out on the client or asked to reauthenticate using reauthentication APIs provided by the Firebase Authentication client SDKs.

আপনার প্ল্যাটফর্মের জন্য অ্যাডমিন 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.
    }
  });

জাভা

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

পাইথন

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)

সি#

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

ক্লায়েন্টের উপর টোকেন প্রত্যাহার সাড়া দিন

অ্যাডমিন 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!' })
      });
    }
  });
});