Аутентифицировать Firebase с помощью Auth0 с помощью лямбда-функций Netlify - PullRequest
0 голосов
/ 07 февраля 2019

У меня есть веб-приложение, созданное с помощью Gatsby, которое имеет аутентификацию на стороне клиента через Auth0.Я хочу использовать Firebase в качестве базы данных для моего проекта, но мне нужно сначала проверить подлинность пользователей, прежде чем они смогут читать / писать в Firebase.

В Firebase SDK (firebase-admin) есть функция signInWithCustomToken (токен), в которую, как я думал, можно передать токен из Auth0, но это не сработает (см .: https://community.auth0.com/t/react-auth0-firebase/11392).

Вместо этого мне нужно прокси-токен Auth0 через API, который будет использовать firebase-admin для выдачи токена.Так как мой сайт Gatsby размещен на Netlify, я планирую использовать функции Netlify Lambda для получения токена Auth0-токенаВот где я застреваю.

Я следовал этому руководству по использованию лямбда-функций Netlify с Gastsby: https://www.gatsbyjs.org/blog/2018-12-17-turning-the-static-dynamic/

Затем я вошел в свой файл Auth.jsгде мой код Auth0 и отбросил вызов извлечения в setSession. Я передал idToken из Auth0 в 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;

Я нашел учебник под названием Как Aпротестировать Firebase и Angular с помощью 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?

1 Ответ

0 голосов
/ 09 февраля 2019

Я нашел модуль с именем serverless-http , который позволяет мне писать лямбда-функцию, как если бы она была написана в 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-боковая сторона.Надеюсь, это поможет!

...