LoginSignup
1
2

More than 5 years have passed since last update.

kintone上に簡易OAuth2クライアントを作ってみる

Last updated at Posted at 2017-11-19

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週間の有効期限つきです。上記にはリフレッシュトークンを再取得する処理は含まれていません。期限が切れた場合は、認証アプリを操作して再取得する必要があります。
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2