myThings Developers を kintone から使うために簡易的な OAuth2 クライアントを kintone 上に作ってみました。
OAuth2 の認証フローではコールバックの設定と取得したトークンの保存が必要となりますが、これを kintone 上のアプリで実装しています。実用性はあまりなくて、Node.js などを使ったサーバーを構築することなく kintone だけで完結できるかという実験です。
前提
- 今回 OAuth2 の連携先として使用するのは、 myThings Developers です。ハッカソンなどでもお馴染みですね。
- myThings側の設定方法はチュートリアルを参考にしてください。
方針
- kintone 上には OAuth2 認証情報取得用のアプリ(= 認証アプリ)と、実際にリクエストを送信するためのアプリ(= 実行アプリ)の2つを作成します。
- OAuth2 認証フローにおけるコールバックURLとして、認証アプリのURLを擬似的なコールバックURLとして使います。
- レコード編集開始のイベント発火時に kintone 上のjavascript からトークンを取得して、認証アプリに保存します。
- 擬似コールバック(= 認証アプリ)のURLは、情報の特定のためにレコード番号を指定した形となります。
- 実行アプリでは認証アプリに保存されたリフレッシュトークンを使って myThings の API をコールします。
kintone に認証アプリを作る
アプリのデザイン
下記のような構成でフィールドを設定します。
フィールド名 | フィールドコード | タイプ | 用途 |
---|---|---|---|
実行エンドポイント | endPointUrl | 文字列 | myThingsのエンドポイントを入力する |
クライアントID | clientID | 文字列 | OAuth2 のクライアントIDを入力する |
シークレット | secret | 文字列 | OAuth2 のクライアントシークレットを入力する |
リフレッシュトークン | refreshToken | 文字列 | 取得したリフレッシュトークンを保存する |
javascriptの設定
共通部分
getToken.js
const yahooAuthUrl = 'https://auth.login.yahoo.co.jp/yconnect/v1/authorization';
const yahooTokenUrl = 'https://auth.login.yahoo.co.jp/yconnect/v1/token';
const getRedirectUrl = (base) => {
const pathname = location.pathname.replace(base, 'show#mode=edit&record=');
return encodeURI('https://' + location.host + pathname + kintone.app.record.getId());
};
認証コード取得部分
getToken.js
location.href = yahooAuthUrl
+ '?response_type=code'
+ '&client_id=' + dataSet.record.clientID.value
+ '&redirect_uri=' + getRedirectUrl(funcName)
+ '&state=xxxxxxxxxxxxx';
コールバック部分
getToken.js
const getArgs = () => {
// 下記の2パターンを想定
// https://xxxxx.cybozu.com/k/xxx/edit?...
// https://xxxxx.cybozu.com/k/xxx/show#...
const pair = (location.href.substring(location.host.length + location.pathname.length + 9)).split('&');
let args = {};
for(let i = 0; pair[i]; i++) {
const kv = pair[i].split('=');
args[kv[0]] = kv[1];
}
return args;
};
kintone.events.on([
'app.record.create.show'
], (event) => {
const args = getArgs();
if ('code' in args) {
location.href = 'https://' + location.host
+ location.pathname.replace('edit', 'show')
+ '#record=' + args.record
+ '&mode=edit'
+ '&code=' + args.code
+ '&state=' + args.state;
}
});
kintone.events.on([
'app.record.edit.show'
], (event) => {
const args = getArgs();
if ('code' in args) {
//トークン認証
const header = {
'Authorization': 'Basic ' + btoa(event.record.clientID.value + ':' + event.record.secret.value),
'Content-Type': 'application/x-www-form-urlencoded'
};
const body = 'grant_type=authorization_code'
+ '&redirect_uri=' + 'https://' + location.host + location.pathname
+ '&code=' + args.code;
kintone.proxy(yahooTokenUrl, 'POST', header, body).then((response) => {
const responseCode = response[1];
const responseBody = JSON.parse(response[0]);
if (responseCode === 200) {
let dataSet = kintone.app.record.get();
dataSet.record.refreshToken.value = responseBody.refresh_token;
kintone.app.record.set(dataSet);
}
}, (error) => {
alert('トークンの取得でエラーが発生しました。\n' + error.message);
});
}
});
コールバックは、レコード編集画面のURLを指定したいところですが
/show#mode=edit&record=x
みたいなURLとなっているため、myThings側のコールバックURLに設定してもうまく解釈してくれずQuery文字列が欠落した状態で返ってきます(多分 # のところが問題)。そのため、myThingsからのコールバックURLとしてレコードの再利用画面を設定し、そこからリダイレクトする形になっています(うーむ)。
myThings側の事前準備
設定箇所その1
コールバックURL
https://mythings-developers.yahoo.co.jp/sample/confirm
https://<ドメイン名>.cybozu.com/k/<認証アプリのID>/edit?record=<認証アプリのレコード番号>
myThings 側に kintone 上の擬似コールバックのURLを追加します。
設定箇所その2
IPアドレス登録
103.79.0.0/16
CybozuのIPアドレスを設定します。上記の設定は、雑なのでちゃんと調べてくださいね。
kintone の実行アプリから呼び出してみる
アプリのデザイン
下記のような構成でフィールドを設定します。
フィールド名 | フィールドコード | タイプ | 用途 |
---|---|---|---|
通知項目 | 通知項目 | 文字列 | myThingsのエンドポイントに送信するメッセージを登録する |
javascriptの設定
レコード追加時に呼び出すようにしてみます。
setPush.js
const configAppId = <認証アプリのアプリ番号>;
const apiRecNo = <認証情報を取得したレコード番号>;
const yahooTokenUrl = 'https://auth.login.yahoo.co.jp/yconnect/v1/token';
kintone.events.on([
'app.record.create.submit.success'
], (event) => {
return kintoneUtility.rest.getRecord({
'app': configAppId,
'id': apiRecNo
}).then((response) => {
return new kintone.Promise((resolve, reject) => {
let rec = response.record;
//アクセストークンの再取得
const header = {
'Authorization': 'Basic ' + btoa(rec.clientID.value + ':' + rec.secret.value),
'Content-Type': 'application/x-www-form-urlencoded'
};
const body = 'grant_type=refresh_token'
+ '&refresh_token=' + rec.refreshToken.value;
kintone.proxy(yahooTokenUrl, 'POST', header, body).then((response) => {
const responseCode = response[1];
const responseBody = JSON.parse(response[0]);
if (responseCode === 200) {
//トークン取得に成功
const header = {
'Content-Type' : 'application/x-www-form-urlencoded',
'Authorization' : 'Bearer ' + responseBody.access_token
};
const body = 'entry=' + encodeURIComponent(JSON.stringify({
'message': event.record.通知項目.value
}));
resolve({
'success': true,
'url': rec.endPointUrl.value,
'header': header,
'body': body
});
} else {
alert('トークンの取得でエラーが発生しました。\n' + error);
reject();
}
}, (error) => {
alert('トークンの取得でエラーが発生しました。\n' + error);
reject();
});
}).then((result) => {
if (result.success) {
return new kintone.Promise((resolve, reject) => {
// myThings にカスタムトリガーを送信
kintone.proxy(result.url, 'POST', result.header, result.body).then((response) => {
resolve(response);
}, (error) => {
alert('トリガーの送信でエラーが発生しました。\n' + error);
reject();
});
}).then((result) => {
console.log(response[1]);
return event;
});
}
});
});
});
kintone Utility を使用しているので、アプリの設定画面で読込む設定をしてください。
kintoneアプリの設定
https://kintone.github.io/kintoneUtility/kintoneUtility.min.js
ソース一式
注意事項
- 上記の処理では<>などの一部の文字が送信できません。
- kintone や myThings Developers の仕様が変わった場合に動作しなくなることがあるでしょう。
- 実際に運用する場合はやはり OAuth2 を処理する外部 API を作った方が便利でしょう。
- myThings Developers のリフレッシュトークンは4週間の有効期限つきです。上記にはリフレッシュトークンを再取得する処理は含まれていません。期限が切れた場合は、認証アプリを操作して再取得する必要があります。