jdc*_*zle 7 javascript jquery web-applications firebase
我希望新会议基本上"退出"任何以前的会话.例如,当您在一台计算机上进行经过身份验证的会话时,在另一台计算机上启动新会话并在我们的应用程序上使用firebase进行身份验证将在第一台计算机上注销另一个会话.
我无法找到任何允许我"远程"退出会话的方法.我知道我可以在会话中使用unauth()和goOffline().但是,如何从同一用户的其他经过身份验证的会话中执行此操作?
谢谢您的帮助!
背景资料:
Kat*_*ato 10
一般的想法是,您希望在Firebase中创建一些元数据,告诉您用户登录的位置数.然后,您可以使用此信息限制其访问权限.
为此,您需要生成自己的令牌(以便您的安全规则可以使用这些信息).
1)生成令牌
使用自定义登录生成您自己的令牌.每个令牌应包含客户端的唯一ID(IP地址?UUID?)
var FirebaseTokenGenerator = require("firebase-token-generator");
var tokenGenerator = new FirebaseTokenGenerator(YOUR_FIREBASE_SECRET);
var token = tokenGenerator.createToken({ id: USER_ID, location_id: IP_ADDRESS });
Run Code Online (Sandbox Code Playgroud)
2)使用在线存储用户的location_id
查看管理在线入门详情:
var fb = new Firebase(URL);
// after getting auth token back from your server
var parts = deconstructJWT(token);
var ref = fb.child('logged_in_users/'+token.id);
// store the user's location id
ref.set(token.location_id);
// remove location id when user logs out
ref.onDisconnect().remove();
// Helper function to extract claims from a JWT. Does *not* verify the
// validity of the token.
// credits: https://github.com/firebase/angularFire/blob/e8c1d33f34ee5461c0bcd01fc316bcf0649deec6/angularfire.js
function deconstructJWT(token) {
var segments = token.split(".");
if (!segments instanceof Array || segments.length !== 3) {
throw new Error("Invalid JWT");
}
var claims = segments[1];
if (window.atob) {
return JSON.parse(decodeURIComponent(escape(window.atob(claims))));
}
return token;
}
Run Code Online (Sandbox Code Playgroud)
3)添加安全规则
在安全规则中,强制只有当前唯一位置才能读取数据
{
"some_restricted_path": {
".read": "root.child('logged_in_users/'+auth.id).val() === auth.location_id"
}
}
Run Code Online (Sandbox Code Playgroud)
4)控制对logged_in_users的写访问
您需要设置一些控制对logged_in_users的写访问权限的系统.显然,用户应该只能写入自己的记录.如果您希望第一次登录尝试始终获胜,则在使用时阻止写入值(直到它们注销)".write": "!data.exists()"
但是,您可以通过允许上次登录获胜来大大简化,在这种情况下,它会覆盖旧的位置值,之前的登录将无效并且无法读取.
5)这不是控制同步数量的解决方案
您无法使用此功能来防止Firebase出现多个并发现象.请参阅goOffline()和goOnline()以获取有关完成此操作的更多数据(或获取付费计划,以便您无需连接上限).
TL;DR
We've come across this topic as well. Despite the thread's obsolescence and the fact that it doesn't entirely outline our exact same desire we wanted to achieve, yet we could soak some of the general concepts of @kato's answer up. The conceptions have roughly remained the same but this thread definitely deserves a more up-to-date answer.
Heads-up: before you read this explanation right off the bat, be aware of the fact you'll likely find it a bit out of context because it doesn't entirely cover the original SO question. In fact, it's rather a different mental model to assemble a system to prevent multiple sessions at the same time. To be more precise, it's our mental model that fits our scenario. :)
For example, when you are in an authenticated session in one computer, starting a new session on another computer and authenticating with firebase on our app will log out the other session on the first computer.
Maintaining this type of "simultaneous-login-prevention" implies 1) the active sessions of each client should be differentiated even if it's from the same device 2) the client should be signed out from a particular device which AFAICT Firebase isn't capable of. FWIW you can revoke tokens to explicitly make ALL of the refresh tokens of the specified user expired and, therefore, it's prompted to sign in again but the downside of doing so is that it ruins ALL of the existing sessions(even the one that's just been activated).
These "overheads" led to approaching the problem in a slightly different manner. It differs in that 1) there's no need to keep track of concrete devices 2) the client is signed out programmatically without unnecessarily destroying any of its active sessions to enhance the user experience.
Leverage Firebase Presence to pass the heavy-lifting of keeping track of connection status changes of clients(even if the connection is terminated for some weird reason) but here's the catch: it does not natively come with Firestore. Refer to Connecting to Cloud Firestore to keep the databases in sync. It's also worthwhile to note we don't set a reference to the special .info/connected path compared to their examples. Instead, we take advantage of the onAuthStateChanged() observer to act in response to the authentication status changes.
const getUserRef = userId => firebase.database().ref(`/users/${userId}`);
firebase.auth().onAuthStateChanged(user => {
if (user) {
const userRef = getUserRef(user.uid);
return userRef
.onDisconnect()
.set({
is_online: false,
last_seen: firebase.database.ServerValue.TIMESTAMP
})
.then(() =>
// This sets the flag to true once `onDisconnect()` has been attached to the user's ref.
userRef.set({
is_online: true,
last_seen: firebase.database.ServerValue.TIMESTAMP
});
);
}
});
Run Code Online (Sandbox Code Playgroud)
After onDisconnect() has been correctly set, you'll have to ensure the user's session if it tries kicking off a sign-in alongside another active session, for which, forward a request to the database and check against the corresponding flag. Consequently, recognizing multiple sessions takes up a bit more time than usual due to this additional round-trip, hence the UI should be adjusted accordingly.
const ensureUserSession = userId => {
const userRef = getUserRef(userId);
return userRef.once("value").then(snapshot => {
if (!snapshot.exists()) {
// If the user entry does not exist, create it and return with the promise.
return userRef.set({
last_seen: firebase.database.ServerValue.TIMESTAMP
});
}
const user = snapshot.data();
if (user.is_online) {
// If the user is already signed in, throw a custom error to differentiate from other potential errors.
throw new SessionAlreadyExists(...);
}
// Otherwise, return with a resolved promise to permit the sign-in.
return Promise.resolve();
});
};
Run Code Online (Sandbox Code Playgroud)
Combining these two snippets together results in https://pastebin.com/jWYu53Up.
| 归档时间: |
|
| 查看次数: |
5903 次 |
| 最近记录: |