firebase - Firebase: Sign in with a token
问题描述
Firebase question
My goal is to keep a user signed in a desktop app made primarily with NodeJS
- User signs in
- Store some kind of token to sign user in later
- When user re-opens app, sign user in with that token if available
- Token can expire / be invalidated
Currently, it seems Firebase relies on cookies of some sort. Is there a way to sign in a user without their email/password again?
I am currently trying to use Firebase authentication as part of my Electron app where I cannot have the Firebase authentication code be part of a renderer process which would normally have access to browser-level storage.
The ideal situation would be something like this that works:
const auth_token = await user.getIdToken();
firebase
.auth()
.signInWithCustomToken(auth_token) // But maybe some other method?
.then((user) => {
console.log(user);
})
How would you do this in a NodeJS app that has no access to browser storage such that the user does not need to continuously login?
Referencing this issue: Persist Firebase user for Node.js client application
fwiw, I don't wish to share these tokens with another application.
Electron question
A slightly different question: Can the main process in Electron store cookies that Firebase could access?
Recently read this: https://github.com/firebase/firebase-js-sdk/issues/292
解决方案
我对此的解决方案是:
- 通过电子邮件/密码获取刷新令牌(寿命约 1 年)
- 使用刷新令牌获取 id_token(短期)
- 获取带有 id_token 的自定义令牌(短期:~1 小时)
- 使用自定义令牌登录
- 在本地保存刷新令牌 - 永远不要共享它
所以,像这样:
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);
}
});
推荐阅读
- c# - .NetCore 3.1API 无法加载文件或程序集“Microsoft.AspNetCore.Hosting.Abstractions”
- java - JpaRepository 中的existById 返回 NullPointerException
- python - TypeError: preprocess() 得到了一个意外的关键字参数“shave”
- javascript - 为表中的每个 td 添加唯一的事件侦听器
- css - SVG组中的响应间距?
- unit-testing - AndroidStudio 不会自动导入 RoboLectric 对象
- google-maps - 带有触摸屏的笔记本电脑上标记图标 SVG 路径大小的 Google Maps API 问题
- javascript - 使用 CSS 或 JS 滚动一个 div 比另一个慢
- javascript - OpenCV JS/WASM 构建尝试添加人脸模块失败
- python - 如何将base64解码从python2转换为python3