octonodeを使ってGitHubに2段階認証ログインしてみた(!!!Deprecated!!!)


[octonode](https://github.com/pksunkara/octonode)という、GitHubAPIをNode.jsから触るためのライブラリを使ってみている。

octonodeのreadmeにあるログイン認証のサンプルコードを使って、

```javascript
var scopes = {
  'scopes': ['user', 'repo', 'gist'],
  'note': 'admin script'
};

github.auth.config({
  username: 'pksunkara',
  password: 'password'
}).login(scopes, function (err, id, token, headers) {
  console.log(id, token);
});
```

`login`のコールバックにどんな情報が返ってくるのかいろいろ試してみる

## 注意
※[公式blog](https://developer.github.com/changes/2020-02-14-deprecating-password-auth/)によるとIDとパスワードによる認証は近々廃止されるらしいので注意。

octonodeを使う場合は、`login`ではなく同じくreadmeにあるこちらを使うのが良さそう。

```javascript
var client = github.client('someaccesstoken');

client.get('/user', {}, function (err, status, body, headers) {
  console.log(body); //json object
});
```

## ID間違い（存在しないユーザーID）の場合

```:err
Error: Bad credentials
    at Request._callback (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/octonode/lib/octonode/auth.js:90:31)
    at Request.self.callback (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/request/request.js:185:22)
    at Request.emit (events.js:310:20)
    at Request.<anonymous> (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:310:20)
    at IncomingMessage.<anonymous> (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:416:28)
    at IncomingMessage.emit (events.js:322:22)
    at endReadableNT (_stream_readable.js:1187:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)
```

```:id
null
```

```:token
null
```

```:headers
{
  date: 'Sat, 15 Aug 2020 13:02:13 GMT',
  'content-type': 'application/json; charset=utf-8',
  'content-length': '80',
  connection: 'close',
  server: 'GitHub.com',
  status: '401 Unauthorized',
  'x-github-media-type': 'github.v3; format=json',
  'x-ratelimit-limit': '60',
  'x-ratelimit-remaining': '53',
  'x-ratelimit-reset': '1597498995',
  'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset',
  'access-control-allow-origin': '*',
  'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
  'x-frame-options': 'deny',
  'x-content-type-options': 'nosniff',
  'x-xss-protection': '1; mode=block',
  'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
  'content-security-policy': "default-src 'none'",
  vary: 'Accept-Encoding, Accept, X-Requested-With',
  'x-github-request-id': 'FED1:6C74:B7B096:F64A41:5F37DCD5'
}
```

## パスワード間違い（存在するユーザーID + でたらめなパスワード）の場合

[ID間違い（存在しないユーザーID）の場合](#ID間違い（存在しないユーザーID）の場合)と同じ結果。
`headers`の`x-github-request-id`だけは違ったが、これは同じ呼び出しでも毎回違うので無視

## 正しいIDとパスワードの場合

下記の結果が返ってきて、携帯に認証コードのSMSが送られてきた。

```:err
Error: Must specify two-factor authentication OTP code.
    at Request._callback (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/octonode/lib/octonode/auth.js:90:31)
    at Request.self.callback (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/request/request.js:185:22)
    at Request.emit (events.js:310:20)
    at Request.<anonymous> (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:310:20)
    at IncomingMessage.<anonymous> (/Users/nariakiiwatani/Works/npm_libraries/create-dev-npm-package-react/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:416:28)
    at IncomingMessage.emit (events.js:322:22)
    at endReadableNT (_stream_readable.js:1187:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)
```

```:id
null
```

```:token
null
```

```:headers
{
  date: 'Sat, 15 Aug 2020 13:12:27 GMT',
  'content-type': 'application/json; charset=utf-8',
  'content-length': '155',
  connection: 'close',
  server: 'GitHub.com',
  status: '401 Unauthorized',
  'x-github-otp': 'required; sms',
  'x-github-media-type': 'github.v3; format=json',
  'x-ratelimit-limit': '60',
  'x-ratelimit-remaining': '50',
  'x-ratelimit-reset': '1597498995',
  'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset',
  'access-control-allow-origin': '*',
  'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
  'x-frame-options': 'deny',
  'x-content-type-options': 'nosniff',
  'x-xss-protection': '1; mode=block',
  'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
  'content-security-policy': "default-src 'none'",
  vary: 'Accept-Encoding, Accept, X-Requested-With',
  'x-github-request-id': 'CE1A:6C74:B7F350:F6A592:5F37DF3A'
}
```

2段階認証のためのOTP(One Time Password)が必要ですよというエラーメッセージと、ヘッダーをよくみると`'x-github-otp': 'required; sms',`がついている。
これを判定に使って良いもんなのかなーと思いつつ`x-github-otp`でググると、公式の説明が見つかった。
[Other Authentication Methods | GitHub Developer Guide](https://developer.github.com/v3/auth/)

> When you make calls to the OAuth Authorizations API, Basic Authentication requires that you use a one-time password (OTP) and your username and password instead of tokens. When you attempt to authenticate with the OAuth Authorizations API, the server will respond with a 401 Unauthorized and one of these headers to let you know that you need a two-factor authentication code:

> `X-GitHub-OTP: required; SMS` or `X-GitHub-OTP: required; app`.

> This header tells you how your account receives its two-factor authentication codes. Depending how you set up your account, you will either receive your OTP codes via SMS or you will use an application like Google Authenticator or 1Password. For more information, see "Configuring two-factor authentication." Pass the OTP in the header:

とのことなので、これを判定に使って良さそう。

試してないけど、二段階認証が無効な場合はIDとパスワードだけで認証が通るんだろう。多分。

ということでログインのフローとしては以下の感じで良さそう。

1. ユーザー名とパスワードを入力してもらう
2. エラーが返ってきたらヘッダーをチェック
    - `X-GitHub-OTP`がなければ「ユーザー名かパスワードが違います」を表示
    - `X-GitHub-OTP`があれば、`SMS`なのか`APP`なのかで、それぞれ適切なメッセージを表示してOTPの入力欄をつくる
3. OTPが入力されたら再度ログインチャレンジ。エラーなら2に戻る

で、OTPを入れてログインしてみた結果はこんな感じで返ってきた。
ちなみにOTPを間違った場合は[正しいIDとパスワードの場合](#正しいIDとパスワードの場合)と同じ結果だった。

## 正しいIDとパスワード（OTP付き）の場合

```:err
null
```

```:id(改変してます)
123456789
```

```:token(改変してます)
hogehogefugafugapiyopiyo
```

```:headers
{
  date: 'Sat, 15 Aug 2020 13:34:22 GMT',
  'content-type': 'application/json; charset=utf-8',
  'content-length': '500',
  connection: 'close',
  server: 'GitHub.com',
  status: '201 Created',
  'x-ratelimit-limit': '5000',
  'x-ratelimit-remaining': '4998',
  'x-ratelimit-reset': '1597499392',
  'cache-control': 'private, max-age=60, s-maxage=60',
  vary: 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding',
  etag: '"719c5159d3e9d7aca3b01c31d8918f1b"',
  location: 'https://api.github.com/authorizations/470351678',
  'x-github-media-type': 'github.v3; format=json',
  deprecation: 'Fri, 14 Feb 2020 16:00:00 GMT',
  sunset: 'Fri, 13 Nov 2020 16:00:00 GMT',
  link: '<https://developer.github.com/changes/2020-02-14-deprecating-oauth-auth-endpoint/>; rel="deprecation"; type="text/html"',
  'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset',
  'access-control-allow-origin': '*',
  'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
  'x-frame-options': 'deny',
  'x-content-type-options': 'nosniff',
  'x-xss-protection': '1; mode=block',
  'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
  'content-security-policy': "default-src 'none'",
  'x-github-request-id': 'EDEE:32E2:BCE233:FCBC08:5F37E45D'
}
```


ちなみにこの方法で取得したトークンをつかって、冒頭の`github.client(token)`はちゃんと通った。
Personal Access Tokenの一覧はGitHubにログインした状態でここから確認/削除ができる。
https://github.com/settings/tokens