Outlook OAuth 2.0 でハマった話

OAuth

http://oauth.net/2/ OAuth2.0の備忘録的まとめ

RESTful APIでは常識になっているOAuthですが、 今回、私はOutlookOAuth認証にハマりました。(´・ω・`)

ことの始まり

Outlookのスケジュールを管理できるアプリ欲しいなという要望のもと、 「Cordovaで簡単にアプリ作っちゃおう!!」 という感じでスタートしました。 この時はまだ、OAuth認証で丸一日以上を費やすとは思ってなかった・・・。 今までにOAuthを使ったAPIを何度か扱ったことはありますが、アクセストークンが取れないということはなかったです。

なぜハマったのか

それは今まで、OAuthライブラリーに頼っていたから。 つまり、実際にどうやってaccess_tokenを取得するかを理解していなかった。 ここで、ライブラリーに頼ってばかりではダメだと痛感しました。 CordovaのOAuthライブラリには、Outlookに対応しているものがなかったので、自前でOAuth認証を実装する必要がありました。 https://github.com/nraboy/ng-cordova-oauth ↑にOutlook(Office365)用のOAuth認証の機能を実装して、プルリクを送っておきます。

実際にコードを見てましょう。

AngularJS 1.5.xで書いています。

...

function oauthOutlook(client_id, client_secret, redirect_uri) {
    var deferred = $q.defer();
    var browser = window.cordova.InAppBrowser.open('https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=' + client_id + '&redirect_uri=' + redirect_uri + '&response_type=code' + '&scope=openid+https%3A%2F%2Foutlook.office.com%2Fcalendars.read', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
    browser.addEventListener('loadstart', function(e) {
        if((e.url).indexOf('code') != -1) {
            var requestToken = (e.url).split("code=")[1];
            if(requestToken) {
                $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                // client_id : client_secret をBase64にエンコード
                $http.defaults.headers.post['Authorization'] = 'Basic ' + window.btoa(client_id + ':' + client_secret);

                $http({method: "post", url: "https://login.microsoftonline.com/common/oauth2/v2.0/token", data:
                    "client_id=" + client_id +
                    "&code=" + requestToken +
                    "&redirect_uri=http://localhost/callback" + 
                    "&client_secret=" + client_secret +
                    "&grant_type=authorization_code" +
                    "&scope=" + "openid+https://outlook.office.com/calendars.read"
                })
                .success(function(data) {
                    deferred.resolve({ access_token: data.access_token});
                    console.log("access_token", data.access_token);
                })
                .error(function(data, status) {
                    deferred.reject("Problem authenticating");
                })
                .finally(function() {
                    setTimeout(function() {
                        browser.close();
                    }, 10);
                });
            }
        } else {
            deferred.reject("please login");
        }
    });

    return deferred.promise;
}
...

参考: Java プログラミングで実装する OAuth 2.0 クライアント: 第 2 回 クライアント・クレデンシャル・グラント