[OAuth2] JavaScriptのみで「Qiita API v2」の認証を処理する


サンプルプログラム https://mofon001.github.io/QiitaAuth/
ソースコード    https://github.com/mofon001/QiitaAuth

認証処理とOAuth2

 Webアプリケーションでは、ユーザー認証にOAuth2が使われることが増えています。各々のアプリがパスワードを管理しなくて良いという利点はあるのですが、認証を突破してトークンを受け取るまでの手順がそれなりに面倒です。今回はそれを最小限のJavaScriptのみで簡単に書いてみます。

Qiita API v2へのアクセス

事前処理

アプリケーションの登録を行う必要があります。
https://qiita.com/settings/applications

項目 内容
アプリケーションの名前 好きな名前
アプリケーションの説明 任意
WebサイトのURL 設置場所
リダイレクト先のURL 設置場所

 
 

プログラムの処理

 処理手順は以下のような流れになります。

 1. APIアクセスに必要なトークンを保持していなければ、認証ページに遷移
 2. 認証後、トークン発行に必要なCODEを受け取る
 3. CODEとシークレットを利用してトークンを発行し、セッションに保存
 4. トークンを使ってAPIを呼び出す

 これをJavaScriptで記述します。
 最大の問題はサーバサイドプログラムを介在させないので、シークレットが隠れないことで。
 しかしモバイルアプリなんかを作る場合も似たり寄ったりな状況なので気にしたら負けです。

ソースコード

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <script type="text/javascript" src="Main.js"></script>
    <title>QiitaAPIv2の認証TokenをJavaScriptのみで取得する</title>
</head>
<body>
</body>
</html>
Main.js
(function(){
var ClientID =     "発行されたID";
var ClientSecret = "発行されたシークレット";
var ClientScope =  "read_qiita";    //権限は必要に応じて
var ClientStat =   "ABC";           //これは適当な文字列でOK

//最初に実行するファンクションを設定
document.addEventListener("DOMContentLoaded",onLoad);

//Json形式のデータを送る
function postJson(url,param,proc) {
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open('POST', url, true);
    xmlHttp.setRequestHeader("Content-Type", "application/json");
    xmlHttp.onreadystatechange = function (){
        if(this.readyState == 4){
            proc(JSON.parse(xmlHttp.response));
        }
    }
    var p = JSON.stringify(param);
    xmlHttp.send(p);
}

//Json形式のデータを受け取る
function getJson(url,token,proc) {
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open('GET', url, true);
    xmlHttp.setRequestHeader('Authorization', 'Bearer ' + token);
    xmlHttp.onreadystatechange = function (){
        if(this.readyState == 4){
            proc(JSON.parse(xmlHttp.response));
        }
    }
    xmlHttp.send();
}

//Token取得処理
function getToken(func){
    //セッションに保存済みか確認
    var token = sessionStorage.getItem("TOKEN");
    if(token != null){
        func(token);    //Token取得後の処理へ
        return;
    }

    //パラメータの取得
    var p = {};
    location.search.substring(1).split('&').forEach(function(v){s=v.split('=');p[s[0]]=s[1];});
    //CODEが送られてきているか?
    if(p.code != null && p.state == ClientStat){
        //パラメータがみっともないのでURLから取り除く
        history.replaceState(null,null,'?');    
        //トークンを取得
        postJson("https://qiita.com/api/v2/access_tokens",  
            {client_id:ClientID,client_secret:ClientSecret,code:p.code},
            function(e){
                sessionStorage.setItem("TOKEN",e.token);    //セッションに保存
                func(e.token);                              //Token取得後の処理へ
            });
    }else{//認証サイトへ遷移
        location.href = "https://qiita.com/api/v2/oauth/authorize?client_id="+
                ClientID+"&scope="+ClientScope+"&state="+ClientStat;
    }       
}

//ページが読み込まれた際に最初に呼び出される
function onLoad(){
    //トークンの要求
    getToken(function(token){
        //トークンをテストするため、ユーザ情報を取得して表示
        getJson("https://qiita.com/api/v2/authenticated_user",token,function(r){
            var table = document.createElement('table');
            table.border = "1";
            Object.keys(r).forEach(function(key){
                var row = table.insertRow(-1);
                row.insertCell(-1).innerHTML = key;
                row.insertCell(-1).innerHTML = r[key];
            });
            document.body.appendChild(table);
        });
    });
}

})();

実行画面

認証

結果表示

終わりに

 ページ遷移しないWebシステムを普及させるために活動しているのですが、OAuth2だけは別のサイトを介在させるため遷移させないのは無理でした。IFrameなんかで抜け道を探ったこともありますが、MicrosoftやGoogleで実験したところ、抜け道チェックが働いていて、みごとに弾かれました。出来ないものは諦めるしかありません。そのかわり、出来るだけ簡単に実装するところに主眼を置きました。

 JavaScriptで色々なWebサービスのAPIを利用したい。でもOAuth2が面倒だと思っている方の参考になれば幸いです。