React Native as Twitter Client - OAuth1.0＋fetchでタイムラインを取得する

こんにちは。昨年は一人ReactNativeカレンダーをやりましたが、今年は業務が忙しくて無理そうなので、このカレンダーに参加させていただきます:)

さて、FacebookやTwitterでログインするモジュールは沢山ありますが、そのtokenを利用してFeedを取得する方法となると極端に情報がなくなります。今回はReact NativeでTwitterのフィードを取得するため方法を解説したいと思います。というより、むしろOauth1.0aをどうやって通すかの説明がメインになると思います。

まず、 twitterのトークンを取得しましょう。これは、[react-native-oauth](https://github.com/fullstackreact/react-native-oauth)などを使えば比較的容易に取得できると思います。この手の多くがNativeにリンクするかたちで実現しているので、ドキュメントがしっかりしたものを選ぶといいでしょう。この点、`react-native-ouath`はしっかりしたREADMEがあるので利用しやすいかと思います。ただiOSの場合は、PODを利用して、インストールするので将来的にPODで管理したくない人には不向きかもしれません。

一度、インストールしてしまえば、下記のように`creadentials`を取得できます。 私はこのファイルと`/app/utils/Auth.js`みたいに保存して利用してます。

```
const config = {
  twitter: {
    consumer_key: '*',
    consumer_secret: '*',
  },
  facebook: {
    client_id: '*',
    client_secret: '*'
  },
}

// Create the manager
const manager = new OAuthManager('myApp')
// configure the manager
manager.configure(config);

module.exports = {

  authenticate(providerName) {
    return new Promise((resolve, reject) => {
      let promise; 
      switch (providerName) {
        case 'twitter':
          promise = manager.authorize('twitter');
          break;
        case 'facebook':
          promise = manager.authorize('facebook', {scopes: 'user_posts'});        
          break;         
        default:
          console.log('Unknown provider');
          break;
      }
      if(promise){
        promise.then((res) => {
          resolve({ credentials: res.response.credentials, uuid: res.response.uuid });
        }).catch((error) =>{
          reject(error);
        });
      }else{
        reject('unknown provider');
      }
    });
  }
  
}
```

Twitterの場合、OAuth1.0aなので、Credential取得後にすることは、Oauthヘッダやそれに使われるSignatureの作成を行う必要があります(twitterの[ドキュメント](https://dev.twitter.com/oauth/overview/authentication-by-api-family)に詳しく記載されています）。これは最近の言語ならライブラリやモジュールで実装されているのですが、ReactNativeの場合、Nodeのコアライブラリでハッシュ計算とかをしてるのでNodeのモジュールで使えません。色々試してみたところ、[ohauth](https://github.com/osmlab/ohauth)というのがPure JSで実装されており、一部の機能をTwitter用に書き換えれば使えることがわかりました。

結論からいうと、nonceの32文字の書き換えぐらいでしたが、ここまでに何度[Oauth signature checker](http://quonos.nl/oauthTester/)を通したことでしょう^^; 

Headerを作るコードは下記で、

```
var ohauth = require('ohauth');
...

 generateTwitterOauthHeader(method, url, urlParams, token, tokenSecret){    

    let headerParams = {
      oauth_consumer_key: config.twitter.consumer_key,
      oauth_nonce: this._getNonce(),
      oauth_signature_method: "HMAC-SHA1",
      oauth_timestamp: ohauth.timestamp(),
      oauth_token: token,
      oauth_version: "1.0"
    };    

    headerParams = Object.assign(headerParams, urlParams);
    console.log(headerParams);

    let baseString = ohauth.baseString(method, url, headerParams)
    console.log(baseString);

    let sig = ohauth.signature(config.twitter.consumer_secret, tokenSecret, baseString) // function(oauth_secret, token_secret, baseString) {
    console.log(sig)

    headerParams["oauth_signature"] = sig;
    let header = ohauth.authHeader(headerParams);
    console.log(header);    

    return header
  },
  
    _getNonce(){
    for (var o = ''; o.length < 32;) {
      o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
    }
    return o;    
  }
```

これを`fetch()`のheaderに差し込んで使います。

```
import Auth from './Auth';

const homeTimelineUrl = 'https://api.twitter.com/1.1/statuses/home_timeline.json';

module.exports = {

  getHomeTimeLine(token, tokenSecret){
    return new Promise((resolve, reject) => {

      let oauthHeader = Auth.generateTwitterOauthHeader('GET', homeTimelineUrl, {count: 100}, token, tokenSecret);

      fetch(`${homeTimelineUrl}?count=100`, 
      {
        method: 'GET',
        headers: {
          'Authorization':  'OAuth '+oauthHeader,
        }
      }
      )
      .then((response) => response.json())
      .then((responseJson) => {
        console.log(responseJson);
        resolve(responseJson);
      })
      .catch((error) => {
        console.log(error);
        reject(error);
      });
    });
  }

}
```

できてしまえば簡単ですが、Oauth1.0は面倒臭いですね。昔同じようなことを書いたのを思い出しました。