Monaca Docs
検索
K

Twitter アプリ

ここでは、Twitter 上でのOAuthを利用したシングルサインオン ( Single Sign-On/SSO ) の方法を解説します。 シングルサインオンには、InAppBrowser プラグインと、サードパーティ製 advanced-http プラグイン を使用し、また、OAuthに必要なシグネチャは oauth-signature ライブラリ を利用します。
認証成功後、ユーザーの基本情報をアプリ上に表示したり、ツイートを投稿することが出来ます。
サードパーティー製 Cordova プラグインを確認する場合は、カスタムビルドデバッガー ( Android 版 または iOS 版 ) を作成する必要があります。

デモ

テスト環境
  • Android 11.0
  • iOS 14.3

事前準備

Twitter の Consumer Key と Consumer Secret の確認方法

TwitterAPIを利用するためには、デベロッパーアカウントの登録が必要となります。 Twitter Developer Account よりデベロッパーアカウントを登録します。
次に、『 Twitter Developer Portal 』ページ上で、アプリを登録し、Consumer Key (API key ) と Consumer Secret (API secret key) 、Bearer token を発行します。
  1. 2.
    Standalone Apps を、画面下部の Create App ボタンより作成します。 (Standalone Apps を作成する代わりに、Projects配下にAppsを作成でも構いません)
  2. 3.
    Name ( アプリ名 )、Description ( アプリの説明 )、Website ( アプリのダウンロード元となる URL )を入力します。(※ アプリ名には、すでに利用されている名前は利用できません。また、1日あたりに作成出来るアプリ数には、上限があります。)
  3. 4.
    「Callback URL」(任意:認証成功後に表示されるページ。)を入力します。 今回のサンプルアプリでは、 mymonacaapp:// と設定します。適時、自分のアプリ用のものに変更してください。 なお、このCallback URLはアプリの実装時にも必要になります。
  4. 5.
    Settings タブを選択して、 App permissions から、 Read and Write 権限を許可します。アプリからツイートを行わない場合は、Read 権限のみ許可します。
  5. 6.
    Settings タブを選択して、 Authentication から、 3-legged OAuth を有効にします。

プラグインのインポート

シングルサインオンを実現させるため、InAppBrowser プラグインと サードパーティ製 advanced-http プラグイン を使用します。 また、OAuthに必要なシグネチャを作成するために oauth-signatureライブラリを使用します。
  1. 1.
    Monaca クラウド IDE から 設定 → Cordova プラグインの管理 を選択します。
  2. 2.
    InAppBrowser プラグインを 有効 にします。
3. ボタン「Cordovaプラグインのインポート」をクリックして、「URLもしくはパッケージ名を指定します」にチェックをし、パッケージ名に cordova-plugin-advanced-http を入力し、OKボタンをクリックします。

JSコンポーネントのインポート

  1. 1.
    Monaca クラウド IDE から 設定 → JS/CSSコンポーネントの追加と削除 を選択します。
  2. 2.
    コンポーネントの検索フォームに oauth-signature-js と入力して検索。検索結果から oauth-signature-js をインストールします。その後、読み込み対象の選択にて、 components/oauth-signature-js/dist/oauth-signature.js を選択し、保存します。 なお、oauth-signature.jsライブラリは、 BSD-3-Clause であるので、アプリのソースコード、バイナリコードの配布を行う場合はご注意下さい。

アプリの解説

ファイル構成

ファイル
説明
index.html
アプリ画面のページ
css/style.css
アプリのスタイルシート
js/app.js
アプリの実行時にさまざまな処理を行う JavaScript ファイル

HTML の解説

index.html

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="Content-Security-Policy" content="default-src * data: gap: content: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
<script src="components/loader.js"></script>
<link rel="stylesheet" href="components/loader.css">
<link rel="stylesheet" href="css/style.css">
<script src="js/app.js"></script>
</head>
<body>
<h2>Twitter Sample</h2>
<div>
<button onclick="connect()">Connect !</button>
<p>Your Id:
<span id="tw-id"></span>
</p>
<p>Your Name:
<span id="tw-name"></span>
</p>
<hr>
<button id="showMe" onclick="showMe()">Show Me</button>
<p>Your Info:
<span id="tw-profile"></span>
</p>
<img id="tw-profile-image" class="profile">
<hr>
<p>
<textarea id="tweetText" rows=5 cols=40>Hello</textarea>
</p>
<button id="tweetBtn" onclick="sendTweet()">Send Tweet</button>
</div>
</body>
</html>
アプリ画面となるページです。
このページは、大きく3つのブロックに分かれています。それぞれのブロックは、 <hr> タグにより区切られています
  1. 1.
    ログイン ブロック:
    Twitterログイン画面に移行するボタンと、ログイン後にユーザーID、ユーザー名(スクリーン名)を表示するコンポーネントがあります。
  2. 2.
    プロフィール ブロック:
    ログインユーザーのプロフィールを取得するボタンと、取得成功後にそれを表示するコンポーネントがあります。
  3. 3.
    ツイート ブロック:
    テキストエリアと、ツイートするボタンがあります。

JavaScript の解説

app.js

const apiKey = '【Consumer Key(API key)】';
const secretKey = '【Consumer Secret (API key secret)】';
const callbackURL = '【登録したコールバックURL(スキーム)】';
const signatureMethod = "HMAC-SHA1";
const version = "1.0";
const requestTokenURL = "https://api.twitter.com/oauth/request_token";
const loginURL = "https://api.twitter.com/oauth/authorize"
const accessTokenURL = "https://api.twitter.com/oauth/access_token";
const updateURL = "https://api.twitter.com/1.1/statuses/update.json";
const usersShowURL = "https://api.twitter.com/1.1/users/show.json";
let model = {};
function percentEncode(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, char => '%' + char.charCodeAt().toString(16));
}
function getNonce() {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return Array.from(array).map(uint => uint.toString(16).padStart(2, '0')).join('');
}
function oauthSend(url, method, accessTokenSecret, data, oauth_params, cb) {
const timestamp = (Math.floor(Date.now() / 1000)).toString(10);
const parameters = Object.assign(
{},
oauth_params,
{
oauth_nonce: getNonce(),
oauth_signature_method: signatureMethod,
oauth_timestamp: timestamp,
oauth_version: version
},
data
);
const signature = oauthSignature.generate(method.toUpperCase(),
url,
parameters,
secretKey,
accessTokenSecret
);
const authorizationHeader = "OAuth " + Object.keys(parameters).map((key) => {
return percentEncode(key) + '="' + percentEncode(parameters[key]) + '", '
}).join('') + 'oauth_signature=\"' + signature + '\"';
const urlWithParams = method.toUpperCase() === "GET" ?
url + "?" + Object.keys(data).map(function(k) {
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
}).join('&') : url;
cordova.plugin.http.sendRequest(urlWithParams, {
method: method,
headers: {'Authorization': authorizationHeader},
data: data,
serializer: null
}, (res) => {
cb(res);
}, (error) => {
alert(error.error);
});
}
function connect() {
oauthSend(requestTokenURL, 'post', "", {}, {
oauth_callback: callbackURL,
oauth_consumer_key: apiKey,
}, function (res) {
openLoginDialog(res.data);
});
}
function openLoginDialog(res) {
const oauth = res.split('&')[0];
const url = loginURL + "?" + oauth;
const ref = cordova.InAppBrowser.open(url, "_blank", 'location=yes,beforeload=get');
ref.addEventListener('beforeload', beforeLoad(ref));
}
function beforeLoad(ref) {
return function (event, cb) {
if (event.url && event.url.startsWith(callbackURL) ) {
const url = new URL(event.url);
const params = Array.from(url.searchParams.entries()).reduce(
function (acc, cur) {
acc[cur[0]] = cur[1];
return acc;
} , {}
);
ref.close();
getAccessToken(params);
} else {
cb(event.url);
}
};
}
function getAccessToken(params) {
oauthSend(accessTokenURL, 'post', "", {}, {
oauth_verifier: params.oauth_verifier,
oauth_token: params.oauth_token,
oauth_consumer_key: apiKey,
}, function (res) {
const params = Array.from(new URLSearchParams(res.data).entries()).reduce(
function (acc, cur) {
acc[cur[0]] = cur[1];
return acc;
} , {}
);
document.getElementById('tw-id').innerHTML = params.user_id;
document.getElementById('tw-name').innerHTML = params.screen_name;
model.oauth_token = params.oauth_token;
model.oauth_token_secret = params.oauth_token_secret;
model.user_id = params.user_id;
});
}
function sendTweet() {
const text = document.querySelector("#tweetText").value;
oauthSend(updateURL, 'post', model.oauth_token_secret, { "status": text }, {
oauth_token: model.oauth_token,
oauth_consumer_key: apiKey
}, function (res) {
alert("Tweet しました");
});
}
function showMe() {
oauthSend(usersShowURL, 'get', model.oauth_token_secret, { "user_id": model.user_id }, {
oauth_token: model.oauth_token,
oauth_consumer_key: apiKey
}, function (res) {
const profile = JSON.parse(res.data);
document.querySelector("#tw-profile").innerHTML =
"name: " + profile["name"] + "<br>" +
"screen_name: " + profile["screen_name"] + "<br>" +
"location: " + profile["location"] + "<br>" +
"description: " + profile["description"] + "<br>";
document.querySelector("#tw-profile-image").src = profile["profile_image_url_https"].replace("_normal", "");
});
}
ファイルの最初3行で、事前準備で取得した APIキー(コンシューマキー)、APIキーシークレット(コンシューマシクレット)、コールバックURLを設定します。
const apiKey = '【API key(コンシューマキー)】';
const secretKey = '【API key secret(コンシューマシークレット)】';
const callbackURL = "【登録したコールバックURL(スキーム)】";
Connectボタンをタップすると認証画面が表示されます。
ログイン成功後、アクセストークンとユーザーIDを変数 model に保持します。 今回のサンプルアプリでは、ログアウト機能はないため、ログイン状態を解除するには、アプリをタスクからkillすることが必要です。
次に Show Me ボタンをタップすると、ログインしたユーザー情報と、アイコンが表示されます。 さらに、Send Tweet ボタンをタップすると、テキストフォームに入力したメッセージが、Twitterへ投稿されます。
Twitterのログイン画面にて、パスワードを「保持します(Remember me)」にチェックした場合、次回からアカウントとパスワードは、入力せずにログイン済みの状態で認可画面になります。Twitterからログアウトしたい場合は、認可画面で右上のアイコンから、サインアウトして下さい。
このアプリでは、ログイン情報(アクセストークン)を 変数modelに一時的に保持しているため、アプリを再起動するとログイン情報は失われてしまいます。ログインしている状態を維持したい場合は、アクセストークンとユーザーIDをlocalstorage等の永続化可能な場所に保存します。