Javascript 当需要来自另一个js文件的数据时,验证代码失败。但在同一个文件中工作-为什么?

Javascript 当需要来自另一个js文件的数据时,验证代码失败。但在同一个文件中工作-为什么?,javascript,firebase,cookies,authorization,spotify,Javascript,Firebase,Cookies,Authorization,Spotify,仍然是一个未解之谜。任何找到答案的人现在都将保存我的项目 我有一个需要Spotify提供凭据的应用程序。我正在使用模板()通过Spotify登录,但我正在更改它,以便从Firestore数据库检索Spotify客户端id和客户端机密凭据 为了保存凭证以便在整个主index.js文件中使用,我通过spotify.js文件导出数据,并将其存储在index.js文件的const中 除了exports.token函数之外,这里的一切都可以工作:这是一个存储身份验证代码(从地址栏)然后在Authoriza

仍然是一个未解之谜。任何找到答案的人现在都将保存我的项目

我有一个需要Spotify提供凭据的应用程序。我正在使用模板()通过Spotify登录,但我正在更改它,以便从Firestore数据库检索Spotify客户端id和客户端机密凭据

为了保存凭证以便在整个主index.js文件中使用,我通过spotify.js文件导出数据,并将其存储在index.js文件的const中

除了exports.token函数之外,这里的一切都可以工作:这是一个存储身份验证代码(从地址栏)然后在AuthorizationCodeGrant中使用身份验证代码的函数。这最终会得到一个访问令牌和一个刷新令牌。(请参阅下面的popup.html了解代码中的外观。所有这些都没有更改,但可能是问题的原因)

出于某种原因,使用的身份验证代码在AuthorizationCodeGrant上返回为“无效”,这意味着我无法获得访问权或刷新令牌。见下面的代码:

index.js

'use strict';

const functions = require('firebase-functions');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');

// Firebase Setup
const admin = require('firebase-admin');
const serviceAccount = require('./service-account.json');
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,
});

// Spotify OAuth 2 setup
const spotify = require('./spotify');

// TODO: Configure the `spotify.client_id` and `spotify.client_secret` Google Cloud environment variables.
const SpotifyWebApi = require('spotify-web-api-node');


// Scopes to request.
const OAUTH_SCOPES = ['user-read-email'];

/**
 * Redirects the User to the Spotify authentication consent screen. Also the 'state' cookie is set for later state
 * verification.
 */
exports.redirect = functions.https.onRequest((req, res) => {
  cookieParser()(req, res, () => {
    const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
    console.log('Setting verification state:', state);
    res.cookie('state', state.toString(), {maxAge: 3600000, secure: true, httpOnly: true});
    spotify.then(Spotify =>{
      console.log('This is the client id used in redirect ' + Spotify.getClientId());
      console.log('This is the client secret used in redirect ' + Spotify.getClientSecret());
      const authorizeURL = Spotify.createAuthorizeURL(OAUTH_SCOPES, state.toString());
      res.redirect(authorizeURL);
    })

  });
});

/**
 * Exchanges a given Spotify auth code passed in the 'code' URL query parameter for a Firebase auth token.
 * The request also needs to specify a 'state' query parameter which will be checked against the 'state' cookie.
 * The Firebase custom auth token is sent back in a JSONP callback function with function name defined by the
 * 'callback' query parameter.
 */
exports.token = functions.https.onRequest((req, res) => {
  try {

    cookieParser()(req, res, () => {
      console.log('Received verification state:', req.cookies.state);
      console.log('Received state:', req.query.state);
      if (!req.cookies.state) {
        throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
      } else if (req.cookies.state !== req.query.state) {
        throw new Error('State validation failed');
      }
      console.log('Received auth code:', req.query.code);
      spotify.then(Spotify =>{
        Spotify.authorizationCodeGrant(req.query.code, (error, data) => {
          if (error) {
            throw error;
          }
          console.log('Received Access Token:', data.body['access_token']);
          Spotify.setAccessToken(data.body['access_token']);

          Spotify.getMe(async (error, userResults) => {
            if (error) {
              throw error;
            }
            console.log('Auth code exchange result received:', userResults);
            // We have a Spotify access token and the user identity now.
            const accessToken = data.body['access_token'];
            const spotifyUserID = userResults.body['id'];
            const profilePic = userResults.body['images'][0]['url'];
            const userName = userResults.body['display_name'];
            const email = userResults.body['email'];

            // Create a Firebase account and get the Custom Auth Token.
            const firebaseToken = await createFirebaseAccount(spotifyUserID, userName, profilePic, email, accessToken);
            // Serve an HTML page that signs the user in and updates the user profile.
            res.jsonp({token: firebaseToken});
          });
        });
      })
    });
  } catch (error) {
    return res.jsonp({error: error.toString});
  }
  return null;
});

/**
 * Creates a Firebase account with the given user profile and returns a custom auth token allowing
 * signing-in this account.
 * Also saves the accessToken to the datastore at /spotifyAccessToken/$uid
 *
 * @returns {Promise<string>} The Firebase custom auth token in a promise.
 */
async function createFirebaseAccount(spotifyID, displayName, photoURL, email, accessToken) {
  // The UID we'll assign to the user.
  const uid = `spotify:${spotifyID}`;

  // Save the access token to the Firebase Realtime Database.
  const databaseTask = admin.database().ref(`/spotifyAccessToken/${uid}`).set(accessToken);

  // Create or update the user account.
  const userCreationTask = admin.auth().updateUser(uid, {
    displayName: displayName,
    photoURL: photoURL,
    email: email,
    emailVerified: true,
  }).catch((error) => {
    // If user does not exists we create it.
    if (error.code === 'auth/user-not-found') {
      return admin.auth().createUser({
        uid: uid,
        displayName: displayName,
        photoURL: photoURL,
        email: email,
        emailVerified: true,
      });
    }
    throw error;
  });

  // Wait for all async tasks to complete, then generate and return a custom auth token.
  await Promise.all([userCreationTask, databaseTask]);
  // Create a Firebase custom auth token.
  const token = await admin.auth().createCustomToken(uid);
  console.log('Created Custom token for UID "', uid, '" Token:', token);
  return token;
}
正如我提到的,当细节在同一个文件中时,这个问题不会发生。有人知道为什么cookie解析器部分会因为数据来自另一个文件而中断吗?更奇怪的是,授权代码确实工作得非常好——我已经在邮递员身上测试过了。但是它们在代码中是无效的——不知道为什么!我想这可能是空白的问题,但这也不是,有什么想法吗?明晚有这个项目,所以在这一点上变得非常疯狂

下面是调用重定向(工作)和令牌(不工作)函数的popup.html:

<!doctype html>
<!--
  Copyright 2016 Google Inc. All rights reserved.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
      https://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License
-->
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="description" content="Demonstrates how to authorize Firebase with Spotify auth using Firebase Functions">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Authenticate with Spotify</title>
</head>
<body>

Please wait...
<!-- Firebase -->
<script src="/__/firebase/6.4.1/firebase-app.js"></script>
<script src="/__/firebase/6.4.1/firebase-auth.js"></script>
<script src="/__/firebase/init.js"></script>

<script>
  /**
   * Returns the value of the given URL query parameter.
   */
  function getURLParameter(name) {
    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) ||
        [null, ''])[1].replace(/\+/g, '%20')) || null;
  }

  /**
   * Returns the ID of the Firebase project.
   */
  function getFirebaseProjectId() {
    return firebase.app().options.authDomain.split('.')[0];
  }

  /**
   * This callback is called by the JSONP callback of the 'token' Firebase Function with the Firebase auth token.
   */
  function tokenReceived(data) {
    if (data.token) {
      firebase.auth().signInWithCustomToken(data.token).then(function() {
        window.close();
      });
    } else {
      console.error(data);
      document.body.innerText = 'Error in the token Function: ' + data.error;
    }
  }

  var code = getURLParameter('code');
  var state = getURLParameter('state');
  var error = getURLParameter('error');
  if (error) {
    document.body.innerText = 'Error back from the Spotify auth page: ' + error;
  } else if(!code) {
    // Start the auth flow.
    window.location.href  = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/redirect';
  } else {
    // Use JSONP to load the 'token' Firebase Function to exchange the auth code against a Firebase custom token.
    const script = document.createElement('script');
    script.type = 'text/javascript';
    // This is the URL to the HTTP triggered 'token' Firebase Function.
    // See https://firebase.google.com/docs/functions.
    var tokenFunctionURL = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/token';
    script.src = tokenFunctionURL +
        '?code=' + encodeURIComponent(code) +
        '&state=' + encodeURIComponent(state) +
        '&callback=' + tokenReceived.name;
    document.head.appendChild(script);
  }
</script>
</body>
</html>

通过Spotify验证
请稍候。。。
/**
*返回给定URL查询参数的值。
*/
函数getURLParameter(名称){
返回decodeURIComponent((新的RegExp('[?|&]'+name+'='+'([^&;]+?)(&|#| | | |$))。exec(location.search)||
[null,][1]。替换(/\+/g,'%20'))| null;
}
/**
*返回Firebase项目的ID。
*/
函数getFirebaseProjectId(){
返回firebase.app().options.authDomain.split('.')[0];
}
/**
*此回调由带有Firebase auth令牌的“token”Firebase函数的JSONP回调调用。
*/
接收到的函数标记(数据){
if(data.token){
firebase.auth(){
window.close();
});
}否则{
控制台错误(数据);
document.body.innerText='令牌函数中的错误:'+data.Error;
}
}
var code=getURLParameter('code');
var state=getURLParameter('state');
var error=getURLParameter('error');
如果(错误){
document.body.innerText='从Spotify验证页面返回的错误:'+错误;
}否则如果(!代码){
//启动auth流。
window.location.href=https://us-central1-“+getFirebaseProjectId()+”.cloudfunctions.net/redirect”;
}否则{
//使用JSONP加载“token”Firebase函数,以针对Firebase自定义令牌交换身份验证代码。
const script=document.createElement('script');
script.type='text/javascript';
//这是HTTP触发的“令牌”Firebase函数的URL。
//看https://firebase.google.com/docs/functions.
var-tokenFunctionURL=https://us-central1-“+getFirebaseProjectId()+”.cloudfunctions.net/token”;
script.src=tokenFunctionURL+
“?代码=”+encodeURIComponent(代码)+
“&state=”+encodeURIComponent(状态)+
“&callback=”+tokenReceived.name;
document.head.appendChild(脚本);
}


控制台输出和错误消息是什么?错误消息是{[WebAPIRROR:Bad Request]name:'WebAPIRROR',消息:'Bad Request',状态代码:400}尽管身份验证代码实际上在邮递员身上工作。所以我真的不确定是什么导致了这个糟糕的请求。与原始版本相比,唯一改变的是使用“spotify.then(spotify…”,而不仅仅是使用“spotify.authorizationCodeGrant(req.query.code,(error,data)=>{if(error){throw error;}“但我的意思是,你有很多控制台消息,可以帮助我们跟踪逻辑。客户端ID和密码是否确实正确记录?只需仔细观察代码,
让客户端ID=JSON.stringify(snapshot.data().client_ID);
看起来可疑。我使用firebase已经有一段时间了,但我希望使用
snapshot.val()
或者可能是
snapshot.toJSON()
。啊,我记得为什么我现在把它作为snapshot.data().client\u id。这是因为对于firestore(不是实时firebase),这是访问值必须写入的内容。放置val()似乎不起作用
<!doctype html>
<!--
  Copyright 2016 Google Inc. All rights reserved.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
      https://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License
-->
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="description" content="Demonstrates how to authorize Firebase with Spotify auth using Firebase Functions">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Authenticate with Spotify</title>
</head>
<body>

Please wait...
<!-- Firebase -->
<script src="/__/firebase/6.4.1/firebase-app.js"></script>
<script src="/__/firebase/6.4.1/firebase-auth.js"></script>
<script src="/__/firebase/init.js"></script>

<script>
  /**
   * Returns the value of the given URL query parameter.
   */
  function getURLParameter(name) {
    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) ||
        [null, ''])[1].replace(/\+/g, '%20')) || null;
  }

  /**
   * Returns the ID of the Firebase project.
   */
  function getFirebaseProjectId() {
    return firebase.app().options.authDomain.split('.')[0];
  }

  /**
   * This callback is called by the JSONP callback of the 'token' Firebase Function with the Firebase auth token.
   */
  function tokenReceived(data) {
    if (data.token) {
      firebase.auth().signInWithCustomToken(data.token).then(function() {
        window.close();
      });
    } else {
      console.error(data);
      document.body.innerText = 'Error in the token Function: ' + data.error;
    }
  }

  var code = getURLParameter('code');
  var state = getURLParameter('state');
  var error = getURLParameter('error');
  if (error) {
    document.body.innerText = 'Error back from the Spotify auth page: ' + error;
  } else if(!code) {
    // Start the auth flow.
    window.location.href  = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/redirect';
  } else {
    // Use JSONP to load the 'token' Firebase Function to exchange the auth code against a Firebase custom token.
    const script = document.createElement('script');
    script.type = 'text/javascript';
    // This is the URL to the HTTP triggered 'token' Firebase Function.
    // See https://firebase.google.com/docs/functions.
    var tokenFunctionURL = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/token';
    script.src = tokenFunctionURL +
        '?code=' + encodeURIComponent(code) +
        '&state=' + encodeURIComponent(state) +
        '&callback=' + tokenReceived.name;
    document.head.appendChild(script);
  }
</script>
</body>
</html>