Firebase 问题
我的目标是让用户在主要使用 NodeJS 制作的桌面应用程序中保持登录状态
- 用户登录
- 存储某种 token 以供用户稍后登录
- 当用户重新打开应用时,使用该 token (如果可用)让用户登录
- token 可以过期/失效
目前,Firebase 似乎依赖于某种 cookie。有没有办法在没有 Electron 邮件/密码的情况下再次登录用户?
我目前正在尝试将 Firebase 身份验证用作我的 Electron 应用程序的一部分,我无法让 Firebase 身份验证代码成为通常可以访问浏览器级存储的渲染器进程的一部分。
理想情况应该是这样的:
const auth_token = await user.getIdToken();
firebase
.auth()
.signInWithCustomToken(auth_token) // But maybe some other method?
.then((user) => {
console.log(user);
})
您如何在无法访问浏览器存储的 NodeJS 应用程序中执行此操作,这样用户就不需要持续登录?
引用这个问题:Persist Firebase user for Node.js client application
首先,我不想与其他应用程序共享这些 token 。
Electron 题
一个稍微不同的问题:Electron 中的主进程是否可以存储 Firebase 可以访问的 cookie?
最佳答案
我的解决方案是:
- 通过 Electron 邮件/密码获取刷新 token (长期有效~1 年)
- 获取带有刷新 token 的 id_token(短期)
- 获取带有 id_token 的自定义 token (短暂:~1 小时)
- 使用自定义 token 登录
- 在本地保存刷新 token - 从不共享
所以,像这样:
import Store from 'electron-store';
import firebase from 'firebase';
import * as request from 'request-promise';
const store = new Store({ name: 'config' });
function logout() {
const user = firebase.auth().currentUser;
store.delete('refresh_token');
if (user) {
return firebase.auth().signOut();
}
}
// https://firebase.google.com/docs/reference/rest/auth#section-create-email-password
function signup(email, password) {
return request
.post({
headers: { 'content-type': 'application/x-www-form-urlencoded' },
url: `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${firebase_config['apiKey']}`,
body: `email=${email}&password=${password}&returnSecureToken=true`,
json: true,
})
.then((res) => {
store.set({ refresh_token: res.refreshToken });
return login_with_id_token(res.idToken);
});
}
// Generates a refresh_token that we later use & save
async function login_with_email(email: string, password: string) {
const res = await request.post({
headers: { 'content-type': 'application/x-www-form-urlencoded' },
url: `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${firebase_config['apiKey']}`,
body: `email=${email}&password=${password}&returnSecureToken=true`,
json: true,
});
const refresh_token = res.refreshToken;
store.set({ refresh_token });
console.log(store.path);
return login_with_refresh_token(refresh_token);
}
/**
* Needed to acquire a refresh_token
* @param refresh_token string
*/
function get_id_token(refresh_token) {
return request.post({
headers: { 'content-type': 'application/x-www-form-urlencoded' },
url: `https://securetoken.googleapis.com/v1/token?key=${firebase_config['apiKey']}`,
body: `grant_type=refresh_token&refresh_token=${refresh_token}`,
json: true,
});
}
/**
* Generates a custom token we can use to sign in given an id_token
* @param id_token string
*/
function get_custom_token(id_token) {
return request.get({
url: `https://us-central1-${firebase_config['projectId']}.cloudfunctions.net/create_custom_token?id_token=${id_token}`,
json: true,
});
}
function login_with_id_token(id_token) {
if (id_token) {
return get_custom_token(id_token).then((token) => {
// console.log(`Retrieved custom token: ${custom_token}`);
return firebase.auth().signInWithCustomToken(token);
});
}
}
/**
* If token is null, it attempts to read it from disk otherwise
* it will use the one supplied to login
* @param token string || null
*/
async function login_with_refresh_token(token = null) {
let id_token = null;
let refresh_token = token;
if (!refresh_token) {
refresh_token = store.get('refresh_token');
store.get('refresh_token', null);
// console.log('Using a cached refresh token...');
}
if (refresh_token) {
const res = await get_id_token(refresh_token);
if (res) {
id_token = res['id_token'];
return login_with_id_token(id_token);
}
}
}
// Purposely attempt to login without a refresh_token in case it's on disk
function attempt_login() {
return login_with_refresh_token(null);
}
Firebase 云函数:
exports.create_custom_token = functions.https.onRequest(async (req, res) => {
const id_token = req.query.id_token;
const user = await admin.auth().verifyIdToken(id_token);
if (user) {
const custom_token = await admin.auth().createCustomToken(user.uid);
res.setHeader('Content-Type', 'application/json');
res.status(200).send(custom_token);
} else {
res.sendStatus(500);
}
});
关于Firebase:使用 token 登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58110378/