使用Netlify Lambda函数通过Auth0对Firebase进行身份验证

时间:2019-02-07 18:22:50

标签: firebase lambda auth0

我有一个使用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 Functions来获取代理Auth0的令牌。这就是我被困住的地方。

我已经按照本教程介绍如何在Gastsby上使用Netlify Lambda函数:https://www.gatsbyjs.org/blog/2018-12-17-turning-the-static-dynamic/

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

import React, {useState, useContext, useEffect} from 'react';
import TodosContext from './context'
import { withRouter } from 'react-router-dom';

function Todos(props){   
    const [todo, setTodo] = useState("")
    const {state, dispatch} = useContext(TodosContext)
    useEffect(()=>{
        if(state.currentTodo.text){
            setTodo(state.currentTodo.text)
        } else {
            setTodo("")
        }
        dispatch({
            type: "GET_TODOS",
            payload: state.todos
        })
    }, [state.currentTodo.id])

    const editRow = event =>{
        let destUrlEdit = `/todos/${event.id}`

        let obj = {}
        obj.id = event.id
        obj.text = event.text

        dispatch({type:"SET_CURRENT_TODO", payload: obj})

        //after dispatch I would like to redirect to another route to do the actual edit
        //destUrlEdit
        props.history.push(destUrlEdit);
    }
    return(
        <div>
            <h1>List of ToDos</h1>
            <h4>{title}</h4>
            <ul>
                {state.todos.map(todo => (
                    <li key={todo.id}>{todo.text} &nbsp;
                        <button onClick={()=>{
                            editRow(todo)}}>
                        </button>
                    </li>
                ))}
            </ul>
        </div>
    )
}

export default withRouter(Todos);

我发现了一个名为How to Authenticate Firebase and Angular with Auth0的教程,该教程具有为Firebase铸造令牌的功能:

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;

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

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
        })
      );
  });

我尝试通过控制台记录日志来查看jwtCheck的返回结果,但是我得到的只是一个奇怪的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), }), }) }

我应该如何将其合并到Lambda函数中?

1 个答案:

答案 0 :(得分:0)

我找到了一个名为serverless-http的模块,该模块使我可以像在Express中一样编写Lambda函数。这使我很容易就可以回避正在发生的事情,所以我终于有了这段代码来从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客户端中执行所需的操作。希望这会有所帮助!