获取 ForbiddenError:无效的 csrf 令牌(使用 firebase auth、autodesk forge 和 node.js)

Posted

技术标签:

【中文标题】获取 ForbiddenError:无效的 csrf 令牌(使用 firebase auth、autodesk forge 和 node.js)【英文标题】:Getting ForbiddenError: invalid csrf token (Working with firebase auth, autodesk forge, and node.js) 【发布时间】:2021-04-11 06:39:48 【问题描述】:

在使用 firebase auth 和 Autodesk forge 时遇到问题。我使用 csurf 创建用户会话的 cookie,以便使用 firebase 身份验证。实施 csurf 后,我得到错误,为什么我尝试使用 Autodesk forge 功能之一:

responseText: ""message":"invalid csrf token","code":"EBADCSRFTOKEN""

来自我的forgeTree文件,第55行(第55行所在的函数在下面用**突出显示),代码如下:

$(document).ready(function () 
  prepareAppBucketTree();
  $('#refreshBuckets').click(function () 
    $('#appBuckets').jstree(true).refresh();
  );

  $('#createNewBucket').click(function () 
    createNewBucket();
  );

  $('#createBucketModal').on('shown.bs.modal', function () 
    $("#newBucketKey").focus();
  );

  $('#hiddenUploadField').change(function () 
    var node = $('#appBuckets').jstree(true).get_selected(true)[0];
    var _this = this;
    if (_this.files.length == 0) return;
    var file = _this.files[0];
    switch (node.type) 
      case 'bucket':
        var formData = new FormData();
        formData.append('fileToUpload', file);
        formData.append('bucketKey', node.id);

        $.ajax(
          url: '/api/forge/oss/objects',
          data: formData,
          processData: false,
          contentType: false,
          type: 'POST',
          success: function (data) 
            $('#appBuckets').jstree(true).refresh_node(node);
            _this.value = '';
          
        );
        break;
    
  );
);

**function createNewBucket() 
  var bucketKey = $('#newBucketKey').val();
  jQuery.post(
    url: '/api/forge/oss/buckets',
    contentType: 'application/json',
    data: JSON.stringify( 'bucketKey': bucketKey ),
    success: function (res) 
      $('#appBuckets').jstree(true).refresh();
      $('#createBucketModal').modal('toggle');
    ,
    error: function (err) 
      if (err.status == 409)
        alert('Bucket already exists - 409: Duplicated');
      console.log(err);
    
  );
**

function prepareAppBucketTree() 
  $('#appBuckets').jstree(
    'core': 
      'themes':  "icons": true ,
      'data': 
        "url": '/api/forge/oss/buckets',
        "dataType": "json",
        'multiple': false,
        "data": function (node) 
          return  "id": node.id ;
        
      
    ,
    'types': 
      'default': 
        'icon': 'glyphicon glyphicon-question-sign'
      ,
      '#': 
        'icon': 'glyphicon glyphicon-cloud'
      ,
      'bucket': 
        'icon': 'glyphicon glyphicon-folder-open'
      ,
      'object': 
        'icon': 'glyphicon glyphicon-file'
      
    ,
    "plugins": ["types", "state", "sort", "contextmenu"],
    contextmenu:  items: autodeskCustomMenu 
  ).on('loaded.jstree', function () 
    $('#appBuckets').jstree('open_all');
  ).bind("activate_node.jstree", function (evt, data) 
    if (data != null && data.node != null && data.node.type == 'object') 
      $("#forgeViewer").empty();
      var urn = data.node.id;
      getForgeToken(function (access_token) 
        jQuery.ajax(
          url: 'https://developer.api.autodesk.com/modelderivative/v2/designdata/' + urn + '/manifest',
          headers:  'Authorization': 'Bearer ' + access_token ,
          success: function (res) 
            if (res.status === 'success') launchViewer(urn);
            else $("#forgeViewer").html('The translation job still running: ' + res.progress + '. Please try again in a moment.');
          ,
          error: function (err) 
            var msgButton = 'This file is not translated yet! ' +
              '<button class="btn btn-xs btn-info" onclick="translateObject()"><span class="glyphicon glyphicon-eye-open"></span> ' +
              'Start translation</button>';
            $("#forgeViewer").html(msgButton);
          
        );
      );
    
  );


function autodeskCustomMenu(autodeskNode) 
  var items;

  switch (autodeskNode.type) 
    case "bucket":
      items = 
        uploadFile: 
          label: "Upload file",
          action: function () 
            uploadFile();
          ,
          icon: 'glyphicon glyphicon-cloud-upload'
        
      ;
      break;
    case "object":
      items = 
        translateFile: 
          label: "Translate",
          action: function () 
            var treeNode = $('#appBuckets').jstree(true).get_selected(true)[0];
            translateObject(treeNode);
          ,
          icon: 'glyphicon glyphicon-eye-open'
        
      ;
      break;
  

  return items;


function uploadFile() 
  $('#hiddenUploadField').click();


function translateObject(node) 
  $("#forgeViewer").empty();
  if (node == null) node = $('#appBuckets').jstree(true).get_selected(true)[0];
  var bucketKey = node.parents[0];
  var objectKey = node.id;
  jQuery.post(
    url: '/api/forge/modelderivative/jobs',
    contentType: 'application/json',
    data: JSON.stringify( 'bucketKey': bucketKey, 'objectName': objectKey ),
    success: function (res) 
      $("#forgeViewer").html('Translation started! Please try again in a moment.');
    ,
  );

这是我的 start.js 文件,它定义了 csrf 中间件:

const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const path = require('path');
const bodyParser = require("body-parser");
const express = require('express');
const ejs = require("ejs");
const admin = require("firebase-admin");

var serviceAccount = require("./serviceAccountKey.json");

admin.initializeApp(

  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://firebasedatabaselink.com"
);



const PORT = process.env.PORT || 3000;
const config = require('./config');


if (config.credentials.client_id == null || config.credentials.client_secret == null)

  console.error('Missing FORGE_CLIENT_ID or FORGE_CLIENT_SECRET env. variables.');
  return;


const app = express();


app.use('/public', express.static(path.join(__dirname, 'public')));
app.set("view engine", "ejs");

app.use(cookieParser());
app.use(bodyParser.json());
const csrfMiddleware = csrf(
  
    cookie: true
  );
app.use(csrfMiddleware);

app.use(express.json(

  limit: '50mb'
));
app.use('/api/forge/oauth', require('./routes/oauth'));
app.use('/api/forge/oss', require('./routes/oss'));
app.use('/api/forge/modelderivative', require('./routes/modelderivative'));
app.use((err, req, res, next) =>

  console.error(err);
  res.status(err.statusCode).json(err);
);

app.all("*", (req, res, next) =>

  res.cookie("XSRF-TOKEN", req.csrfToken());
  next();
);


app.get("/", function(req, res)

  res.render("login");
);

app.get("/register", function(req, res)

  res.render("register");
);

app.get("/view", function(req, res)

  const sessionCookie = req.cookies.session || "";

  admin
    .auth()
    .verifySessionCookie(sessionCookie, true /** checkRevoked */ )
    .then(() =>
    
      res.render("view");
    )
    .catch((error) =>
    
      res.render("login");
    );
);

app.post("/sessionLogin", (req, res) =>

  const idToken = req.body.idToken.toString();

  const expiresIn = 60 * 60 * 24 * 5 * 1000;

  admin
    .auth()
    .createSessionCookie(idToken,
    
      expiresIn
    )
    .then(
      (sessionCookie) =>
      
        const options = 
          maxAge: expiresIn,
          httpOnly: true
        ;
        res.cookie("session", sessionCookie, options);
        res.end(JSON.stringify(
        
          status: "success"
        ));
      ,
      (error) =>
      
        res.status(401).send("UNAUTHORIZED REQUEST!");
      
    );
);

app.get("/sessionLogout", (req, res) =>

  res.clearCookie("session");
  res.redirect("/login");
);


app.listen(PORT, () =>

  console.log(`Server listening on port $PORT`);
);

这里是 firebaseauthentication.js 文件:

  const signupForm = document.querySelector('#signup-form');
  const logout = document.querySelector('#logout');
  const loginForm = document.querySelector('#login-form');

  // listen for auth status changes
  auth.onAuthStateChanged(user =>
  
    if (user)
    
      console.log("user logged in:", user);
    
    else
    
      console.log("user logged out");
    

  );


  if (document.querySelector('#signup-form') !== null)
  
    // register
    signupForm.addEventListener('submit', (e) =>
    
      e.preventDefault();

      // get user info

      const email = signupForm['signup-email'].value;
      const password = signupForm['signup-password'].value;

      // register the user

      auth.createUserWithEmailAndPassword(email, password).then(cred =>
      
        // console.log(cred.user);
        signupForm.reset();
      );
    );
  
  else if (document.querySelector('#login-form') !== null)
  
    document
    .getElementById("login-form")
    .addEventListener("submit", (event) =>
    
      event.preventDefault();
      const login = loginForm['login-email'].value;
      const password = loginForm['login-password'].value;

      firebase
        .auth()
        .signInWithEmailAndPassword(login, password)
        .then((
        
          user
        ) =>
        
          return user.getIdToken().then((idToken) =>
          
            return fetch("/sessionLogin",
            
              method: "POST",
              headers:
              
                Accept: "application/json",
                "Content-Type": "application/json",
                "CSRF-Token": Cookies.get("XSRF-TOKEN"),
              ,
              body: JSON.stringify(
              
                idToken
              ),
            );
          );
        )
        .then(() =>
        
          // loginForm.reset();
          return firebase.auth().signOut();
        )
        .then(() =>
        
          window.location.assign("/view");
        );
      return false;
    );


  
  //logout

  logout.addEventListener('click', (e) =>
  
    e.preventDefault();
    auth.signOut().then(() =>
    
      window.location.assign("/");
    );
  );

还有html集成:

<!-- The core Firebase JS SDK is always required and must be listed first -->

<script src="https://www.gstatic.com/firebasejs/8.2.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.1/firebase-firestore.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie@rc/dist/js.cookie.min.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
  https://firebase.google.com/docs/web/setup#available-libraries -->

<script src="https://www.gstatic.com/firebasejs/8.2.1/firebase-analytics.js"></script>

<script> id="fbauth"
  // Your web app's Firebase configuration
  // For Firebase JS SDK v7.20.0 and later, measurementId is optional
  var firebaseConfig = 
  
    apiKey: "apinumbersgohere",
    authDomain: "firebaseapp.com",
    databaseURL: "https:firebasedatabase.com",
    projectId: "id",
    appId: "id",
    measurementId: "id"
  ;
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();

  // make auth and firestore references
  const auth = firebase.auth();
  const db = firebase.firestore();

  auth.setPersistence(firebase.auth.Auth.Persistence.NONE);

  // update firestore settings
  db.settings(
    timestampsInSnapshots: true
  );
</script>

<p class="text-center text-muted">© Copyright 2020 Obi Vision</p>


<script src="/public/js/fbauth.js"></script>



</body>

</html>

我怀疑 fortree api 以某种方式抓取了我正在生成的 csurf cookie,而不是它自己的实例化 cookie,但我太菜鸟了,无法发现问题出在哪里。任何愿意帮助我的人都会很有帮助,你不知道

【问题讨论】:

【参考方案1】:

Firebase 托管会删除除 __session 之外的所有 cookie,因此您需要在使用会话 cookie 的任何地方进行更改,如下所示,

,,
const sessionCookie = req.cookies.__session || "";
...
res.clearCookie("__session");

下面可能还想更新添加,

app.get("/", function(req, res)

  res.clearCookie("__session");
  res.render("login");
);

Yesterday i had posted similar didnt get any response yet

今天我在不同的页面上遇到了和你一样的错误。

【讨论】:

谢谢你! "res.clearCookie("__session");"成功了! 我试过了,但我还没有足够的积分。你必须让我有幸投票支持我的第一个问题才能给我这个特权

以上是关于获取 ForbiddenError:无效的 csrf 令牌(使用 firebase auth、autodesk forge 和 node.js)的主要内容,如果未能解决你的问题,请参考以下文章

谁知道ssl安全证书哪里下载,怎么获得ssl安全证书?

服务器证书 怎么申请 或者 怎么查询

nodejs.ForbiddenError: invalid csrf token解决方案

ForbiddenError: invalid csrf token, express js.

获取SSL证书private key私钥文件的步骤

为团队创建 iOS 开发证书 - 无效证书