概要
前回はOpenIDで認証を行いました。
ですが実はGoogleのOpenIDでの認証は2015/04までに廃止の予定らしいのでOpenIDConnectへの移行を勧めてるらしいです。
なので今回はOpenIDConnectでの認証を実装します。
環境
- Node.js 0.10.22
- Express 4.0
- passport 0.2.1
GoogleDevelopersConsoleでアプリ作成
OpenIDConnect = OAuth for login なので、ディベロッパーコンソールでアプリを作成します。 https://console.developers.google.com/project/
プロジェクト作成
クライアントIDの作成
左メニューのAPIと認証
の認証情報
をクリックしてください。
クライアントIDの作成をクリック
ウェブアプリケーションを選択して次へ進みます。
最低限以下を設定します。
- メールアドレス
- サービス名
次にコールバック先のURL(リダイレクトURL)を設定します。今回は下記の設定をします。
項目 | 説明 | 今回の例 |
---|---|---|
Javascript生成元 | サイトの URL (パスは含まない) | http://localhost:3000/ |
リダイレクトURL | 認証後のコールバック先 URL | http://localhost:3000/oauth2callback |
設定が完了すると上記の画面が表示されます。このうち
- クライアントID
- クライアントシークレット
- リダイレクトURL
は実装でも使用するのでメモしておいてください。
実装
事前準備
スケルトンコードを生成。
$ express openid-connect $ cd openid-connect
次にライブラリを追加。今回はpassport-openidconnect
を使います。
"passport": "*", "passport-openidconnect": "*", "express-session": "*"
追加したらインストールします。
$ npm install
インストールしたら以下のコードをapp.js
に追記していきます。
ライブラリのインポート
var passport = require('passport'); var session = require('express-session'); var OpenidConnectStrategy = require('passport-openidconnect').Strategy; app.use(passport.initialize()); app.use(passport.session());
passportの設定
clientID
clientSecret
callbackURL
に先ほどGoogleDevelopersConsoleで登録された値を設定します。またscopeは最低限openid
が必要ですが、emailとかも必要であれば
openid
email
profile
あたりがよく利用されるので入れてもいいかもです。
passport.use(new OpenidConnectStrategy({ authorizationURL: "https://accounts.google.com/o/oauth2/auth", tokenURL: "https://accounts.google.com/o/oauth2/token", userInfoURL: "https://www.googleapis.com/oauth2/v1/userinfo", clientID: "クライアントID", clientSecret: "クライアントシークレット", callbackURL: "http://localhost:3000/oauth2callback", scope: ["openid", "email", "profile" ] }, function(accessToken, refreshToken, profile, done) { console.log('accessToken: ', accessToken); console.log('refreshToken: ', refreshToken); console.log('profile: ', profile); return done(null, profile); })); passport.serializeUser(function(user, done){ done(null, user); }); passport.deserializeUser(function(obj, done){ done(null, obj); });
本来OpenIDConnectならID Tokenが返されるべきですが、passportのインタフェースがOAuth2用なせいなのか戻り値は
- accessToken
- refreshToken
- profile
になってますね。後々改善されるのでしょうか。
ルーティング
/auth/google
にアクセスしたら認証が走るようにします。
app.get('/auth/google', passport.authenticate('openidconnect')); app.get('/oauth2callback', passport.authenticate('openidconnect', { failureRedirect: '/login' }), function(req, res) { // Successful authentication, redirect home. res.redirect('/'); });
View
./views/index.jade
にリンクを付けます。
extends layout block content h1= title p Welcome to #{title} a(href="/auth/google") Sign In with Google
以上で設定は完了です。起動しましょう。
$ ./bin/www
動作確認
http://localhost:3000にアクセスしてみます。
リンクをクリックします。
ログインに使う自分のアカウントを選択します。
権限の承認です。承認します。
認証が完了して戻ってきました。
コンソールログを見ると以下のように出力されます。
accessToken: accounts.google.com refreshToken: 102952998963950740274 profile: { id: undefined, displayName: 'FooBar', name: { familyName: 'Foo', givenName: 'Bar', middleName: undefined }, _raw: '{\n "id": "102952998963950740274",\n "email": "cicatrice@gmail.com",\n "verified_email": true,\n "name": "FooBar",\n "given_name": "Bar",\n "family_name": "Foo",\n "link": "https://plus.google.com/102952998963950740274",\n "picture": "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg",\n "gender": "male",\n "locale": "ja",\n "hd": "google.com"\n}\n', _json: { id: '102952998963950740274', email: 'cicatrice@gmail.com', verified_email: true, name: 'FooBar', given_name: 'Bar', family_name: 'Foo', link: 'https://plus.google.com/102952998963950740274', picture: 'https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg', gender: 'male', locale: 'ja', hd: 'google.com' } }
ここでのprofile._json.id
は固有かつ不変の値なので、これをユニークIDとして認証に使うことができます。
ちなみにscopeがopenid
のみの場合は以下のようになります。
accessToken: accounts.google.com refreshToken: 102952998963950740274 profile: { id: undefined, displayName: 'FooBar', name: { familyName: 'Foo', givenName: 'Bar', middleName: undefined }, _raw: '{\n "id": "102952998963950740274",\n "name": "FooBar",\n "given_name": "Bar",\n "family_name": "Foo",\n "link": "https://plus.google.com/102952998963950740274",\n "picture": "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg",\n "gender": "male"\n}\n', _json: { id: '102952998963950740274', name: 'FooBar', given_name: 'Bar', family_name: 'Foo', link: 'https://plus.google.com/102952998963950740274', picture: 'https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg', gender: 'male' } }
以上です。お疲れ様でした。
注意
今回使ったpassport-openidconnect
ですが、本来OpenID Connectで必要な
- 各クレーム(
aud
、nonce
)の検証 - 署名の検証
のどれもがごっそり抜けてます。なのでこのままだとOAuthを用いた認証と同じく脆弱性がある状態なので自前で実装する必要があります。