FJCT_ニフクラ mobile backend(mBaaS)お役立ちブログ

スマホアプリ開発にニフクラ mobile backend(mBaaS)。アプリ開発に役立つ情報をおとどけ!

mBaaSの認証を活用してGitHub認証を行ってみた

※ これはあくまでも実験です。本番環境での利用はお勧めしません。

mBaaSでは認証機能としてID/パスワード認証の他、メールアドレス、ソーシャル(Google/Facebook/Twitter)認証、匿名認証を提供しています。しかし運用上、他の認証(今回はGitHub)にも対応したいと思うこともあるでしょう。今回はそんなチャレンジです。

利用するのは匿名認証

GitHubの認証情報に沿って匿名認証用のIDを生成できれば認証は可能です。なお、匿名認証のIDは下記のようなフォーマットになっています。16進数に適合する0〜9の数字とa-fの文字列が8桁/4桁/4桁/4桁/4桁/12桁とつながって、全体で32桁(と5つの-)となっています。正規表現で表すと次のようになります。

[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}

GitHubの認証情報から、このIDを生成できればNCMBでも認証が可能になります。

GitHub認証を行う

GitHubで認証を行うには、まずGitHubアプリを作ります。作るとクライアントIDとクライアントシークレットの二つの情報が手に入ります。

f:id:mbaasdevrel:20191112110839p:plain

まずクライアントIDを使って、GitHubで認証に関する許可を得ます。その際、ランダムな文字列を渡して(下記state)、レスポンスでそのstateを確認することで、改善されていないことを保証します。

const gitHubAuthUrl = 'https://github.com/login/oauth/authorize';
const clientId = 'Iv1...ea6';
const redirectUri = 'http://localhost:8000/';
const state = Math.random().toString(32).substring(2);
location.href = `${gitHubAuthUrl}?redirect_uri=${redirectUri}&client_id=${clientId}&state=${state}&scope=user`;

コードを取得する

GitHubでアクセスを許可すると認可コードが得られます。これを使ってアクセストークンを得るのですが、次のステップではクライアントシークレットを使います。クライアントシークレットをWebブラウザやクライアントサイドのJavaScriptに記述してしまうのは避けたいので、次の処理はスクリプト機能を使います。クエリストリングからの取得は js-url を使っています。

const code = url('?code');
const state = url('?state');
await ncmb.Script
  .query({
    code: code,
    state: state
  })
  .exec("GET", "githtub.js")

アクセストークンを取得する

ここからはスクリプトの処理です。まず先ほど受け取ったコードを使ってアクセストークンを取得します。

const request = require('superagent');
const clientId = 'Iv1...ea6';
const clientSecret = '160...cd1';
const redirectUri = 'http://localhost:8000/';

const response = await request
  .post('https://github.com/login/oauth/access_token')
  .set('Accept', 'application/json')
  .send({
    client_id: clientId,
    client_secret: clientSecret,
    redirect_uri: redirectUri,
    code: req.query.code,
    state: req.query.state
  });
const json = response.body;
if (!json.access_token) {
  return res.send(json);
}

ユーザ情報を取得する

アクセストークンが得られたら、そのトークンを使ってログインユーザの情報を取得します。この時のTipsとして、ユーザエージェントが必須なので注意してください。アクセストークンはAuthorizationヘッダーに token という文字を頭につけて記述します。

const userResponse = await request
  .get('https://api.github.com/user')
  .set({
    'Accept': 'application/json',
    'User-Agent': 'curl/7.43.0',
    'Authorization': `token ${json.access_token}`,
  })
  .send();
const user = userResponse.body;

これでユーザ情報が手に入りました。

匿名認証用のIDを生成する

問題はここからで、ユーザ情報から匿名認証用のIDを生成する処理が必要です。そこで注目したのがnode_idです。これはGraphQL用のIDとのことで、次のような文字列になります。

MDU6SXNzdWU0MzIzNTg0NzE=

この文字列を下記のように変換すると、32桁の16進数になります。具体的な処理はコードを参照してください。

const code = user.node_id.split("").map(a => a.charCodeAt(0).toString(16)).join("");
// 4d44553653584e7a645755304d7a497a4e5467304e7a453d

32桁の16進数になれば、後は正規表現で桁数に区切るだけです。

const id = num.replace(/([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})/, '$1-$2-$3-$4-$5');
// 4d445536-5358-4e7a-6457-55304d7a497a4e5467304e7a453d

匿名認証を行う

IDが生成できたら、匿名認証を実行します。認証したら、ユーザ情報を返します。

const auth = await ncmb.User.loginAsAnonymous(id);
res.send(ncmb.User.getCurrentUser());

これでGitHubを使った認証処理が完了しました。

まとめ

node_idが不変なものであるか、常に32桁の16進数になるとは限らないので注意してください。とはいえ、IDさえ何らかの条件に従って生成できれば、別な認証プロバイダを組み合わせるのは難しくなさそうです。もちろん、その生成アルゴリズムが漏洩すると、直接認証されてしまう可能性もあるので注意してください。少なくとも生成はサーバサイドで行うべきでしょう。

今回のコードはあくまでも実験ですが、何かの参考になれば幸いです。

中津川 篤司

中津川 篤司

NCMBエヴァンジェリスト。プログラマ、エンジニアとしていくつかの企業で働き、28歳のときに独立。 2004年、まだ情報が少なかったオープンソースソフトの技術ブログ「MOONGIFT」を開設し、毎日情報を発信している。2013年に法人化、ビジネスとエンジニアを結ぶDXエージェンシー「DevRel」活動をスタート。