【问题标题】:Authenticate Firebase with Auth0 using Netlify Lambda Functions使用 Netlify Lambda 函数通过 Auth0 对 Firebase 进行身份验证
【发布时间】:2019-07-01 22:56:05
【问题描述】:

我有一个使用 Gatsby 构建的 Web 应用程序,它通过 Auth0 进行客户端身份验证。我想将 Firebase 用作我的项目的数据库,但我需要先对用户进行身份验证,然后他们才能读取/写入 Firebase。

Firebase SDK (firebase-admin) 有一个名为 signInWithCustomToken(token) 的函数,我认为我可以将令牌从 Auth0 传递到该函数中,但这不起作用(请参阅:https://community.auth0.com/t/react-auth0-firebase/11392)。

相反,我需要通过 API 代理 Auth0 的令牌,该 API 将使用 firebase-admin 发出令牌。因为我的 Gatsby 站点托管在 Netlify 上,所以我计划使用 Netlify Lambda 函数来获取代理 Auth0 的令牌。这就是我卡住的地方。

我已按照本教程学习如何将 Netlify Lambda 函数与 Gastsby 一起使用:https://www.gatsbyjs.org/blog/2018-12-17-turning-the-static-dynamic/

然后我进入了我的 Auth0 代码所在的 Auth.js 文件,并在 setSession 中删除了一个 fetch 调用。我将来自 Auth0 的 idToken 传递到 fetch 函数中的 url 中。我不确定这是否是正确的做法。我在教程中读到它将在授权标头中传递,但我不清楚这意味着什么。无论如何,这是完整的 auth.js 文件:

import auth0 from 'auth0-js';

const windowGlobal = typeof window !== 'undefined' && window;

class Auth {
  auth0 = new auth0.WebAuth({
    domain: process.env.Auth_Domain,
    clientID: process.env.Auth_ClientId,
    redirectUri: process.env.Auth_Callback,
    responseType: 'token id_token',
    scope: 'openid profile email',
  });

  constructor() {
    this.login = this.login.bind(this);
    this.logout = this.logout.bind(this);
    this.handleAuthentication = this.handleAuthentication.bind(this);
    this.isAuthenticated = this.isAuthenticated.bind(this);
  }

  login() {
    this.auth0.authorize();
  }

  logout() {

    // Remove the locally cached profile to avoid confusing errors.
    localStorage.removeItem('access_token');
    localStorage.removeItem('id_token');
    localStorage.removeItem('expires_at');
    localStorage.removeItem('user');

    windowGlobal.window.location.replace(`https://login.skillthrive.com/v2/logout/?returnTo=http%3A%2F%2Flocalhost:8000`)

  }

  handleAuthentication() {
    if (typeof window !== 'undefined') {
      this.auth0.parseHash((err, authResult) => {
        if (authResult && authResult.accessToken && authResult.idToken) {
          this.setSession(authResult)
        } else if (err) {
          console.log(err);
        }
      });
    }
  }

  isAuthenticated() {
    const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    return new Date().getTime() < expiresAt;
  }

  setSession(authResult) {
    const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
    localStorage.setItem('access_token', authResult.accessToken);
    localStorage.setItem('id_token', authResult.idToken);
    localStorage.setItem('expires_at', expiresAt);

    fetch(`/.netlify/functions/firebase?id=${authResult.idToken}`)
    .then(response => console.log(response))

    this.auth0.client.userInfo(authResult.accessToken, (err, user) => {
      localStorage.setItem('user', JSON.stringify(user));
    })
  }

  getUser() {
    if (localStorage.getItem('user')) {
      return JSON.parse(localStorage.getItem('user'));
    }
  }

  getUserName() {
    if (this.getUser()) {
      return this.getUser().name;
    }
  }

}

export default Auth;

我找到了一个名为 How to Authenticate Firebase and Angular with Auth0 的教程,它有一个为 Firebase 铸造令牌的函数:

const jwt = require('express-jwt');
const jwks = require('jwks-rsa');
const firebaseAdmin = require('firebase-admin');
// Config
const config = require('./config');

module.exports = function(app) {
  // Auth0 athentication middleware
  const jwtCheck = jwt({
    secret: jwks.expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: `https://${config.AUTH0_DOMAIN}/.well-known/jwks.json`
    }),
    audience: config.AUTH0_API_AUDIENCE,
    issuer: `https://${config.AUTH0_DOMAIN}/`,
    algorithm: 'RS256'
  });

  // Initialize Firebase Admin with service account
  const serviceAccount = require(config.FIREBASE_KEY);
  firebaseAdmin.initializeApp({
    credential: firebaseAdmin.credential.cert(serviceAccount),
    databaseURL: config.FIREBASE_DB
  });

app.get('/auth/firebase', jwtCheck, (req, res) => {
    // Create UID from authenticated Auth0 user
    const uid = req.user.sub;
    // Mint token using Firebase Admin SDK
    firebaseAdmin.auth().createCustomToken(uid)
      .then(customToken => 
        // Response must be an object or Firebase errors
        res.json({firebaseToken: customToken})
      )
      .catch(err => 
        res.status(500).send({
          message: 'Something went wrong acquiring a Firebase token.',
          error: err
        })
      );
  });

我尝试一次将小部分合并到我的 Lambda 函数中:

var admin = require("firebase-admin");
const jwt = require('express-jwt');
const jwks = require('jwks-rsa');

// For more info, check https://www.netlify.com/docs/functions/#javascript-lambda-functions
export function handler(event, context, callback) {
  console.log("queryStringParameters", event.queryStringParameters);

  const jwtCheck = jwt({
    secret: jwks.expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: `https://${process.env.Auth_Domain}/.well-known/jwks.json`
    }),
    audience: process.env.Auth_Audience,
    issuer: `https://${process.env.Auth_Domain}/`,
    algorithm: 'RS256'
  });

  callback(null, {
    // return null to show no errors
    statusCode: 200, // http status code
    body: JSON.stringify({
      msg: "Hello, World! " + Math.round(Math.random() * 10),
    }),
  })
}

我尝试通过控制台记录 jwtCheck 来检查它返回了什么,但我得到的只是一些奇怪的东西 { [Function: d] unless: [Function], UnauthorizedError: [Function: r] }

我应该如何将它整合到我的 Lambda 函数中?

【问题讨论】:

    标签: firebase lambda auth0


    【解决方案1】:

    我发现了一个名为 serverless-http 的模块,它允许我编写 Lambda 函数,就好像它是用 Express 编写的一样。这让我很容易理解正在发生的事情,所以我终于得到了这段代码来从 Firebase 返回新铸造的令牌:

    const express = require('express');
    const serverless = require('serverless-http');
    const cors = require('cors');
    const jwt = require('express-jwt');
    const jwks = require('jwks-rsa');
    const firebaseAdmin = require('firebase-admin');
    
    const app = express();
    app.use(cors());
    
    const jwtCheck = jwt({
      secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `${process.env.Auth_Domain}/.well-known/jwks.json`
      }),
      audience: `${process.env.Auth_ClientId}`,
      issuer: `${process.env.Auth_Domain}`,
      algorithm: 'RS256'
    });
    
    const serviceAccount = require('../firebase/firebase-keys.json');
    
    firebaseAdmin.initializeApp({
      credential: firebaseAdmin.credential.cert(serviceAccount),
      databaseURL: `https://${serviceAccount.project_id}.firebaseio.com`
    });
    
      // GET object containing Firebase custom token
      app.get('/firebase', jwtCheck, async (req, res) => {
        const {sub: uid} = req.user;
    
        try {
          const firebaseToken = await firebaseAdmin.auth().createCustomToken(uid);
          res.json({firebaseToken});
        } catch (err) {
          res.status(500).send({
            message: 'Something went wrong acquiring a Firebase token.',
            error: err
          });
        }
      });
    
    module.exports.handler = serverless(app);
    
    

    然后在客户端,我将 fetch 调用包装成这样的函数,并在需要时使用它:

      async setFirebaseCustomToken() {
        const response = await fetch('/.netlify/functions/firebase', {
          headers: {
            'Authorization': `Bearer ${localStorage.getItem('id_token')}`,
          },
        });
    
        const data = await response.json();
        console.log(data.firebaseToken);
      }
    
    

    此代码只是用于 console.log 新令牌,但现在您将获得在 Firebase 客户端执行所需操作的响应。希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-29
      • 2015-06-29
      • 1970-01-01
      • 1970-01-01
      • 2019-06-07
      • 2015-04-10
      • 2021-06-26
      • 2023-04-01
      相关资源
      最近更新 更多