繁体   English   中英

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

[英]Getting ForbiddenError: invalid csrf token (Working with firebase auth, autodesk forge, and node.js)

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

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

从第 55 行指示的我的 forgeTree 文件中(第 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>

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

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

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

也可能想在下面更新以添加,

app.get("/", function(req, res)
{
  res.clearCookie("__session");
  res.render("login");
});

昨天发了类似的帖子还没收到回复

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM