Firebase:使用令牌登录

Suh*_*shi 5 firebase electron

Firebase 问题

我的目标是让用户登录主要使用 NodeJS 制作的桌面应用程序

  1. 用户登录
  2. 存储某种令牌以便用户稍后登录
  3. 当用户重新打开应用程序时,使用该令牌(如果可用)登录用户
  4. 令牌可能会过期/失效

目前,Firebase 似乎依赖于某种 ​​cookie。有没有办法让用户无需电子邮件/密码即可再次登录?

我目前正在尝试使用 Firebase 身份验证作为我的 Electron 应用程序的一部分,其中我无法让 Firebase 身份验证代码成为通常可以访问浏览器级存储的渲染器进程的一部分。

理想的情况是这样的:

    const auth_token = await user.getIdToken();

    firebase
      .auth()
      .signInWithCustomToken(auth_token) // But maybe some other method?
      .then((user) => {
        console.log(user);
      })

Run Code Online (Sandbox Code Playgroud)

在无法访问浏览器存储的 NodeJS 应用程序中,您将如何执行此操作,以便用户不需要持续登录?

引用此问题:Persist Firebase user for Node.js client application

fwiw,我不想与其他应用程序共享这些令牌。

电子题

一个稍微不同的问题:Electron 中的主进程可以存储 Firebase 可以访问的 cookie 吗?

最近读过这个:https://github.com/firebase/firebase-js-sdk/issues/292

Suh*_*shi 4

我对此的解决方案是:

  1. 通过电子邮件/密码获取刷新令牌(有效期约 1 年)
  2. 使用刷新令牌获取 id_token(短暂)
  3. 使用 id_token 获取自定义令牌(短暂:约 1 小时)
  4. 使用自定义令牌登录
  5. 在本地保存刷新令牌 - 切勿共享它

所以,像这样:


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);
}
Run Code Online (Sandbox Code Playgroud)

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);
  }
});
Run Code Online (Sandbox Code Playgroud)