2017-12-29 42 views
0

私は、OAuth1.0をよく理解し、Twitter Passportで問題をデバッグするために、Node.jsにTwitterログインを構築しようとしていましたが、 。Twitter OAuth1.0 failed - "Authorization Required"

ドキュメントから、ユーザー認証はトークンの要求から始まります。

私は既に自分のTwitterアプリを作成し、コールバックURLを設定しました。私が経験している問題は、私の署名を作成し、私の最初の要求を提出するときに起こります。

ヘッダーを異なる順番に並べると、400 Bad Requestエラーが発生します。しかし、以下の設定では、401 Authorization Requiredエラーが発生しています。私はどこに私のエラーが見つかりませんか分かりません。私はそれが署名そのものとエンコーディングに関係していると感じます。

私はここでどんな方向にも大いに感謝します。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent - - ポリフィルへ

私のエンコーディングについては

エンコーディング、私はここで見つけたコードのブロックを使用していました。コードは次のように見えた:

function fixedEncodeURIComponent(str) { 
    return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { 
     return '%' + c.charCodeAt(0).toString(16); 
    }); 
} 

しかし、私は結果がencodeURIComponent()を介して実行された文字列であるOAuth npm package、コードベース内のこのかなり独断コメントに出くわした:

// Fix the mismatch between OAuth's RFC3986's and Javascript's beliefs in what is right and wrong ;) 
return result.replace(/\!/g, "%21") 
      .replace(/\'/g, "%27") 
      .replace(/\(/g, "%28") 
      .replace(/\)/g, "%29") 
      .replace(/\*/g, "%2A"); 

私はを選びました後者。

NTPタイム

私はまた、クライアントの時間とサーバー時間の間の可能な切断を述べたので、私はntp-client NPMパッケージを実装し、いくつかのSOの質問をお読みください。

関連するコード

私は何を参照してくださいお知らせください(注:私はmomentaxioscryptoをインポートだ、と私はすべての私の関数をエクスポートするよ、私はちょうどここにこれらの行を除外):

server.js

const nonce = require('./utils/nonce')(); 
const timestamp = require('./utils/timestamp')(); 

Promise.all([nonce, timestamp]).then(function(values) { 

const axios = require('axios'); 
const percentalizedURIComponent = require('./utils/percentalize'); 

const oauth_url = 'https://api.twitter.com/oauth/request_token' 
const oauth_method = 'POST'; 
const oauth_callback = process.env.TWITTER_CALLBACK; 
const oauth_consumer_key = process.env.TWITTER_CONSUMER_KEY; 
const oauth_nonce = values[0]; 
const oauth_signature_method = 'HMAC-SHA1'; 
const oauth_timestamp = values[1]; 
const oauth_version = '1.0'; 
const oauth_signature = require('./utils/signature')(oauth_url, oauth_method, 
    [process.env.TWITTER_CONSUMER_SECRET], 
    { oauth_callback, oauth_consumer_key, oauth_nonce, oauth_signature_method, 
     oauth_timestamp, oauth_version }); 

console.log({ oauth_signature: oauth_signature }); 

axios({ 
    method: oauth_method, 
    url: oauth_url, 
    headers: { 
     Authorization: "OAuth oauth_callback=\"" + 
      percentalizedURIComponent(oauth_callback) + "\", oauth_consumer_key=\"" + 
      percentalizedURIComponent(oauth_consumer_key) + "\", oauth_nonce=\"" + 
      percentalizedURIComponent(oauth_nonce) + "\", oauth_signature=\"" + 
      percentalizedURIComponent(oauth_signature) + "\", oauth_signature_method=\"" + 
      percentalizedURIComponent(oauth_signature_method) + "\", oauth_timestamp=\"" + 
      percentalizedURIComponent(oauth_timestamp) + "\", oauth_version=\"" + 
      percentalizedURIComponent(oauth_version) + "\"", 
     Host: 'api.twitter.com' 
    } 
    }).then(function(response) { 
     console.log({ tokenResponse: response }) 
    }).catch(function(error) { 
     console.error({ twitterTokenError: error }) 
    }); 
}).catch(function(err) { console.error({ createSignatureError: err }) }); 

percentalize.js(符号化)

function percentalizedURIComponent(str) { 
    return encodeURIComponent(str).replace(/\!/g, "%21") 
     .replace(/\'/g, "%27") 
     .replace(/\(/g, "%28") 
     .replace(/\)/g, "%29") 
     .replace(/\*/g, "%2A"); 
} 

timestamp.js

function timestamp() { 
    return new Promise(function(resolve, reject) { 
     ntpClient.getNetworkTime("pool.ntp.org", 123, function(err, date) { 
      if (err) 
       return reject(err); 
      return resolve(parseInt(moment(date).format("X"))); 
     }); 
    }) 
} 

signature.js

/** 
* Function that takes in raw data and outputs a cryptographic signature 
* @param {String} url - endpoint of api being called 
* @param {String} method - HTTP method called on url 
* @param {Object[]} keys - array of signing keys 
* @param {Object} oauth - headers to be encoded into singature 
* @returns {String} hashed signature of data being sent to api 
*/ 
function createSignature(url, method, keys = [], oauth = {}) { 
    const headers = Object.keys(oauth); 
    const paramString = headers.map(function(header) { 
     return percentalizedURIComponent(header) + '=' + percentalizedURIComponent(oauth[header]); 
    }).join("&"); 
    const baseString = method.toUpperCase() + '&' + percentalizedURIComponent(url) + '&' + percentalizedURIComponent(paramString); 
    const hmac = crypto.createHmac(algorithm, keys.join('&')); 
    hmac.update(baseString); 
    return hmac.digest('base64'); 
} 

答えて

0

twitter用OAuth1。0ステップ、いくつかの手順に従う必要があります。

アクセストークンを取得します

  • 認証URLをユーザーに要求トークン
  • オープンTwitterの認証画面を取得し、私は自分のネイティブアプリケーションを反応させるのためのサーバとしてのNode.jsを使用。これらのコードがあなたに役立つことを願っています。

    const express = require('express'); 
    const fetch = require('node-fetch'); 
    const Crypto = require('crypto-js'); 
    
    const app = express(); 
    
    // Twitter keys 
    const twitterConsumerSecret = <YOUR_CONSUMER_SECRET>; 
    const twitterConsumerKey = <YOUR_CONSUMER_KEY>; 
    
    // URL + Routes 
    const requestTokenURL = '/oauth/request_token'; 
    const authorizationURL = '/oauth/authorize'; 
    const accessURL = '/oauth/access_token'; 
    const baseURL = 'https://api.twitter.com'; 
    
    
    // Callback URL of your application, change if standalone. Otherwise this is the one for in Exponent apps. 
    const callbackURL = <YOUR_CALLBACK_URL>/redirect'; 
    
    app.listen(3000,() => { 
        console.log('Twitter login app listening port 3000'); 
    }); 
    
    app.get('/redirect_url', (req, res) => { 
        // Set response to JSON 
        res.setHeader('Content-Type', 'application/json'); 
    
        // Request Token 
        // Creates base header + Request URL 
        const tokenRequestHeaderParams = createHeaderBase(); 
        const tokenRequestURL = baseURL + requestTokenURL; 
    
        // Add additional parameters for signature + Consumer Key 
        tokenRequestHeaderParams.oauth_consumer_key = twitterConsumerKey; 
    
        // Creates copy to add additional request params, to then create the signature 
        const callBackParam = { oauth_callback: callbackURL }; 
        const parametersForSignature = Object.assign({}, callBackParam, tokenRequestHeaderParams); 
        const signature = createSignature(parametersForSignature, 'POST', tokenRequestURL, twitterConsumerSecret); 
        tokenRequestHeaderParams.oauth_signature = signature; 
        // Creates the Header String, adds the callback parameter 
        const headerString = createHeaderString(tokenRequestHeaderParams); 
        const callbackKeyValue = ', oauth_callback="' + encodeURIComponent(callbackURL) + '"'; 
        const tokenRequestHeader = headerString + callbackKeyValue; 
        // Request 
        fetch(tokenRequestURL, { method: 'POST', headers: { Authorization: tokenRequestHeader } }) 
        .then(response => { 
         return response.text(); 
        }).then(response => { 
         const tokenResponse = parseFormEncoding(response); 
         const authToken = tokenResponse.oauth_token; 
         const authTokenSecret = tokenResponse.oauth_token_secret; 
         // Token Authorization, send the URL to the native app to then display in 'Webview' 
         const authURL = baseURL + authorizationURL + '?oauth_token=' + authToken; 
         res.json({ redirectURL: authURL, token: authToken, secretToken: authTokenSecret }); 
        }); 
    }); 
    
    // Requires oauth_verifier 
    app.get('/access_token', (req, res) => { 
        const verifier = req.query.oauth_verifier; 
        const authToken = req.query.oauth_token; 
        const secretToken = req.query.oauth_secret_token; 
        // Creates base header + Access Token URL 
        const accessTokenHeaderParams = createHeaderBase(); 
        const accessTokenURL = baseURL + accessURL; 
    
        // Add additional parameters for signature + Consumer Key 
        accessTokenHeaderParams.oauth_consumer_key = twitterConsumerKey; 
        accessTokenHeaderParams.oauth_token = authToken; 
        accessTokenHeaderParams.oauth_token_secret = secretToken; 
    
        const accessTokenSignature = createSignature(accessTokenHeaderParams, 'POST', accessTokenURL, twitterConsumerSecret); 
        accessTokenHeaderParams.oauth_signature = accessTokenSignature; 
    
        // Creates the Header String, adds the oauth verfier 
        const accessTokenHeaderString = createHeaderString(accessTokenHeaderParams); 
        const verifierKeyValue = ', oauth_verifier="' + encodeURIComponent(verifier) + '"'; 
        const accessTokenRequestHeader = accessTokenHeaderString + verifierKeyValue; 
        // Convert token to Access Token 
        fetch(accessTokenURL, { method: 'POST', headers: { Authorization: accessTokenRequestHeader } }) 
        .then(response => { 
        return response.text(); 
        }).then(response => { 
        const accessTokenResponse = parseFormEncoding(response); 
        res.json({ accessTokenResponse }); 
        }); 
    }); 
    
    /** 
    * Parse a form encoded string into an object 
    * @param {string} formEncoded Form encoded string 
    * @return {Object}    Decoded data object 
    */ 
    function parseFormEncoding(formEncoded) { 
        const pairs = formEncoded.split('&'); 
        const result = {}; 
        for (const pair of pairs) { 
        const [key, value] = pair.split('='); 
        result[key] = value; 
        } 
        return result; 
    } 
    
    /** 
    * Creates the Token Request OAuth header 
    * @param {Object} params OAuth params 
    * @return {string}  OAuth header string 
    */ 
    function createHeaderString(params) { 
        return 'OAuth ' + Object.keys(params).map(key => { 
        const encodedKey = encodeURIComponent(key); 
        const encodedValue = encodeURIComponent(params[key]); 
        return `${encodedKey}="${encodedValue}"`; 
        }).join(', '); 
    } 
    
    /** 
    * Creates the Signature for the OAuth header 
    * @param {Object} params   OAuth + Request Parameters 
    * @param {string} HTTPMethod  Type of Method (POST,GET...) 
    * @param {string} requestURL  Full Request URL 
    * @param {string} consumerSecret Twitter Consumer Secret 
    * @param {?string} tokenSecret Secret token (Optional) 
    * @return {string}     Returns the encoded/hashed signature 
    */ 
    function createSignature(params, httpMethod, requestURL, consumerSecret, tokenSecret) { 
        const encodedParameters = percentEncodeParameters(params); 
        const upperCaseHTTPMethod = httpMethod.toUpperCase(); 
        const encodedRequestURL = encodeURIComponent(requestURL); 
        const encodedConsumerSecret = encodeURIComponent(consumerSecret); 
    
        const signatureBaseString = upperCaseHTTPMethod + 
        '&' + encodedRequestURL + 
        '&' + encodeURIComponent(encodedParameters); 
    
        let signingKey; 
        if (tokenSecret !== undefined) { 
        signingKey = encodedRequestURL + '&' + encodeURIComponent(tokenSecret); 
        } else { 
        signingKey = encodedConsumerSecret + '&'; 
        } 
        const signature = Crypto.HmacSHA1(signatureBaseString, signingKey); 
        const encodedSignature = Crypto.enc.Base64.stringify(signature); 
        return encodedSignature; 
    } 
    
    /** 
    * Percent encode the OAUTH Header + Request parameters for signature 
    * @param {Object} params Dictionary of params 
    * @return {string}  Percent encoded parameters string 
    */ 
    function percentEncodeParameters(params) { 
        return Object.keys(params).map(key => { 
        const encodedKey = encodeURIComponent(key); 
        const encodedValue = encodeURIComponent(params[key]); 
        return `${encodedKey}=${encodedValue}`; 
        }).join('&'); 
    } 
    
    /** 
    * Creates the header with the base parameters (Date, nonce etc...) 
    * @return {Object} returns a header dictionary with base fields filled. 
    */ 
    function createHeaderBase() { 
        return { 
        oauth_consumer_key: '', 
        oauth_nonce: createNonce(), 
        oauth_signature_method: 'HMAC-SHA1', 
        oauth_timestamp: new Date().getTime()/1000, 
        oauth_version: '1.0', 
        }; 
    } 
    
    /** 
    * Creates a nonce for OAuth header 
    * @return {string} nonce 
    */ 
    function createNonce() { 
        let text = ''; 
        const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 
        for (let i = 0; i < 32; i += 1) { 
        text += possible.charAt(Math.floor(Math.random() * possible.length)); 
        } 
        return text; 
    } 
    

    リクエストトークン、トークンシークレット、および認証URLを取得するには、まずredirect_urlを呼び出します。その後、あなたのアプリに許可を与えるようにユーザーに依頼します。次に、これらのパラメータでそのユーザーのアクセストークンを取得します。 oauth_verifier、oauth_token、oauth_secret_token成功した許可のステップの後、twitterはこれらの情報を提供します。その後、私のホストからaccess_token URLを呼び出します。

  • +0

    ありがとうございます。週末にこれを見てみましょう、あなたに戻ってください! – wlh