シングルページアーキテクチャーを使って開発されたWebアプリケーションを目にすることが多くなりました。このアーキテクチャーでは、アプリケーション全体がJavaScriptとしてブラウザーに読み込まれ、サーバーとのやりとりはすべてJSONドキュメントを返すHTTPベースのAPIを使って実行されます。こうしたアプリケーションはユーザープロファイルを保存するときをはじめ、特定のユーザーに操作を限定する必要があります。この処理は従来のHTMLベースのアプリケーションでは比較的簡単に実装できましたが、シングルページアプリケーションではすべてのAPIリクエストを認証しなければならないため、難度が上がります。
本記事では、さまざまなプロバイダーを利用したソーシャルログインをPassport.jsライブラリーで実装する方法を紹介します。さらに、ログイン後のAPIコールで用いるトークンベース認証の実装方法についても説明します。
ソースコードはすべてGitHubリポジトリからダウンロードできます。
SPAでソーシャルログインを使う理由
Webアプリケーションにログイン機能を実装するときに検討する課題があります。
- 認証をユーザーインターフェイスに実装する方法は?
- ユーザー情報を保存する方法は?
- ユーザー認証情報のセキュリティを確保する方法は?
ログイン画面を書き始める前にこれらの課題を解決する必要がありますが、良い方法があります。
ソーシャルネットワークをはじめとする多くのWebサイトが、プラットホームを利用してアプリケーションの認証ができる機能を用意しています。認証に使うAPIにはOAuth 1.0、OAuth 2.0、OpenID、OpenID Connectなどがあります。
ソーシャルログイン技術でログイン機能を実装する利点をまとめます。
- 認証のためのユーザーインターフェイスの作成が不要
- ユーザー機密情報の保存やセキュリティを確保する責任から解放される
- ユーザーは単一のアカウントで複数のWebサイトにログインできる
- パスワードの漏洩が疑われる場合、ユーザーはパスワードを一度再設定すれば多数のWebサイトに適用できる
- 認証機能を提供するサービスを利用するとほかの情報も入手できることが多い。この情報は、当該Webサイトを一度も利用したことがないユーザーを自動的に登録したり、ユーザーの代わりにプロファイルを更新したりするために利用できる
APIにトークンベース認証を使う理由
クライアントがAPIにアクセスしたときに、アクセスを許可するのか判断します。判断する主な方法は以下のとおりです。
- セッションベース認証
- クッキーベース認証
- トークンベース認証
セッションベース認証では、APIサービスがセッションとクライアントを結びつけます。簡単にセットアップできますが、APIを複数のサーバーにデプロイする場合は大変です。また、サーバーのセッション管理メカニズムやセッションの期限切れのコントロールができない場合もあります。
クッキーベース認証ではIDをクッキーに保存し、APIリクエストを自動的に識別します。最初にクッキーを設定する仕組みが必要です。クッキーは同じホストに対するすべてのリクエストに自動的に含まれるため、以降のリクエスト発行時にIDが漏洩する危険があります。
トークンベース認証はクッキーベース認証の派生形ですが、より多くのコントロールができる方法です。トークンを生成し、リクエストにインクルードします(通常は「Authorization」ヘッダー、または直接URLに含めます)。トークンを思いのままにリクエストに格納できるのです。
注記:HTTPヘッダーは「Authorization(認可)」という名前がついていますが、実際はヘッダーを使って「authentication(認証)」をします。クライアントが「なにを」許可されているかを確認するのではなく、クライアントが「誰か」を確認するので認証です。
トークンの生成にどのストラテジーを用いるかも重要です。トークンには参照(reference)トークンと完全(complete)トークンがあります。参照トークンはサーバーが情報を検索するために用いるIDに過ぎません。完全トークンには必要な情報すべてが含まれています。
参照トークンはセキュリティ面で有利です。ユーザー認証の情報がクライアントに漏れる心配がありません。ただし、リクエストのたびにトークンを処理して実際の認証情報を得るためパフォーマンスを犠牲にします。
完全トークンは逆で、トークンの内容を理解できる相手にユーザー認証の情報を知られる恐れがあります。トークンに情報がそろっているので認証情報の取得に伴うパフォーマンスの低下はありません。
完全トークンは、トークンのセキュリティを向上させるために、JSON Web Tokens規格を用いて実装されることが多いです。JWTはトークンを暗号を使って署名するためトークンの改ざんを確実に防止できます。トークンの暗号化のオプションも用意されていて、暗号化キーがなければトークンのデコードさえ不可能です。
Node.jsでJWTを使う方法は、JSON Web TokensをNode.jsで使うを確認してください。
完全トークンの欠点はデータの多さです。参照トークンは、UUIDで実装すれば36文字です。対して、JWTは数百文字に達します。
この記事ではJWTトークンを使います。実際は参照トークンと完全トークンのどちらを使うか、トークンを使うための仕組みを各自検討してください。
Passportとは
PassportはWebアプリケーションに認証機能を実装するためのNode.js用モジュール群です。Node.jsベースのWebサーバーに簡単に導入でき、モジュール構造でログイン機能を実装するためコード量を最小限に抑えられます。
Passportは幅広い認証の要件に対応できる強力なモジュールスイートです。さまざまなエンドポイントの異なる認証要件に合わせてモジュール構成を柔軟に変えられます。Passportはサードパーティのプロバイダーに関する作業をすべて実行するので、認証システムはURL内の特定の値を確認するだけです。
この記事ではpassport-google-oauth、passport-facebook、passport-jwtモジュールを利用します。ソーシャルログインとAPIエンドポイント用のJWTトークンベース認証を実装します。
passport-jwtモジュールは特定のエンドポイント(今回はアクセスする際に認証が必要なAPIエンドポイント)へのリクエスト内に有効なJWTを格納するために使います。passport-google-oauthモジュールとpassport-facebookモジュールはGoogle認証、Facebook認証のためのエンドポイントを設置し、アプリケーションのほかのエンドポイントにアクセスする際に用いるJWTを生成するために使います。
SPAにソーシャルログインを実装する
単純なシングルページアプリケーションにソーシャルログインを実装します。アプリケーションはExpressで書かれていて、セキュアなエンドポイントとセキュアでないエンドポイントを1つずつ提供する単純なAPIを持っています。ソースコードはhttps://github.com/sitepoint-editors/social-logins-spaで確認できます。ダウンロードしたソースコード内でnpm installを実行すればアプリケーションがビルドされ依存オブジェクトがすべてダウンロードされます。node src/index.jsを実行すればアプリケーションが起動します。
アプリケーションを使うにはGoogleとFacebookに登録してソーシャルログインの認証情報を取得し、アプリケーションで利用できるようにします。完全な手順はデモアプリケーションのREADMEファイルにあります。これらの認証情報を環境変数として利用できるようにします。アプリケーションを実行します。
# Linux / OS X
$ export GOOGLE_CLIENTID=myGoogleClientId
$ export GOOGLE_CLIENTSECRET=myGoogleClientSecret
$ export FACEBOOK_CLIENTID=myFacebookClientId
$ export FACEBOOK_CLIENTSECRET=myFacebookClientSecret
$ node src/index.js
# Windows
> set GOOGLE_CLIENTID=myGoogleClientId
> set GOOGLE_CLIENTSECRET=myGoogleClientSecret
> set FACEBOOK_CLIENTID=myFacebookClientId
> set FACEBOOK_CLIENTSECRET=myFacebookClientSecret
> node src/index.js
実施する作業は、セキュアなエンドポイントに(JSON Web Tokenを使った)トークン認証を追加して、GoogleとFacebookを使ったソーシャルログインを追加し、アプリケーションの以降の処理で使用するトークンを取得できるようにします。一度ソーシャルプロバイダーで認証してから、生成したJWTを使ってアプリケーションのAPIを呼び出します。
JWTは情報をすべて内部に格納しつつセキュリティも確保しているため、今回のシナリオに最適です。JWTはJSONペイロードと暗号化された署名で作られています。ペイロードには認証されたユーザー、認証システム、トークンの有効期間についての情報が格納されています。署名鍵でしかトークンを生成できないため、ペイロードが悪意のある第三者がねつ造したものではないことを署名で確認できます。
アプリケーションにインクルードされているconfig.jsモジュールをたびたび参照します。このモジュールはアプリケーションのプロパティを格納するためのモジュールで、Node-convictモジュールを外部から読み込み、構造に利用します。今回使用するプロパティは以下のとおりです。
- http.port:アプリケーションを実行するポート。デフォルトは3000で環境変数「PORT」で上書きする
- authentication.google.clientId:Google認証に使うGoogleのクライアントID。環境変数「GOOGLE_CLIENTID」でアプリケーションに渡す
- authentication.google.clientSecret:Google認証に使うGoogleのクライアントシークレット。環境変数「GOOGLE_CLIENTSECRET」でアプリケーションに渡す
- authentication.facebook.clientId:Facebook認証に使うFacebookのクライアントID。環境変数「FACEBOOK_CLIENTID」でアプリケーションに渡す
- authentication.facebook.clientSecret:Facebook認証に使うFacebookのクライアントシークレット。環境変数「FACEBOOK_CLIENTSECRET」でアプリケーションに渡す
- authentication.token.secret:認証トークンのJWTを署名するために使うシークレットキー(secret)。デフォルトは「mySuperSecretKey」
- authentication.token.issuer:JWT内に保存される発行元(issuer)。複数のアプリケーションを1つの認証サービスでまかなうときに、どのサービスがトークンを発行したかを示す
- authentication.token.audience:JWT内に保存されるオーディエンス(audience)。複数のアプリケーションを1つの認証サービスでまかなうときに、トークンがどのサービスを対象としているかを示す
Passportの組み込み
Passportをアプリケーションで使うためのセットアップ作業は、モジュールをインストールし、Expressアプリケーションでミドルウェアを初期化するだけです。
この段階で必要なモジュールはpassportです。Expressアプリに追加するすることでミドルウェアをセットアップできます。
// src/index.js
const passport = require('passport');
.....
app.use(passport.initialize());
PassportのWebサイトに記載されている手順はpassport.session()を使ってセッションサポートをセットアップしていますが、今回のアプリケーションではステートレスAPIを実装し、認証をセッション期間中持続させずリクエストごと認証するためセッションサポートは使いません。従ってセットアップは不要です。
セキュアなエンドポイントにJWTトークン認証を実装する
JWTトークン認証はPassportを使うことで簡単にセットアップできます。passport-jwtモジュールが面倒な処理をすべて実行します。passport-jwtモジュールは値が「JWT」で始まる「Authorization」ヘッダーを探し、ヘッダーの残りの部分をJWTトークンとして認証に使います。さらにJWTをデコードし内部に保存された値を利用(たとえばユーザーの検索)できるようにします。署名が無効だったり、トークンの期限切れだったりといった理由でJWTが無効なら、コードに追加処理を書かない限り、リクエストは認証されません。
JWTトークン認証を設定します。
// src/authentication/jwt.js
const passport = require('passport');
const passportJwt = require('passport-jwt');
const config = require('../config');
const users = require('../users');
const jwtOptions = {
// Get the JWT from the "Authorization" header.
// By default this looks for a "JWT " prefix
jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeader(),
// The secret that was used to sign the JWT
secretOrKey: config.get('authentication.token.secret'),
// The issuer stored in the JWT
issuer: config.get('authentication.token.issuer'),
// The audience stored in the JWT
audience: config.get('authentication.token.audience')
};
passport.use(new passportJwt.Strategy(jwtOptions, (payload, done) => {
const user = users.getUserById(parseInt(payload.sub));
if (user) {
return done(null, user, payload);
}
return done();
}));
2つの内部モジュールを使っています。
- config.js:アプリケーション全体で使用するプロパティが格納されている。これらのプロパティにはすでに値が設定済みであり、値はいつでも利用できる
- users.js:アプリケーションのユーザーストア。ユーザーの読み込みや作成に使う。ここでは内部IDを使ってユーザーを読み込んでいる
既知のsecret、issuer、audienceを使ってJWTデコーダーを設定し、AuthorizationヘッダーからJWTを取得するストラテジーを作成しています。issuerまたはaudienceがJWT内に格納されたものと一致しない場合は認証に失敗します。とても単純な方法はありますが、これも偽装対策の1つです。
トークンのデコード処理は最初のトークン生成時に使った設定値と同じ値を渡すだけでpassport-jwtモジュールが一手に引き受けます。JWTは規格の1つなので、この規格に従うモジュールはすべて互換性を持っています。
デコードに成功したトークンは、ペイロードとしてコールバックに渡されます。トークンの「subject」でユーザーを特定します。実際のアプリケーションでは、トークンが無効かの確認などチェック処理を追加する必要があるかもしれません。
ユーザーが見つかったらPassportに渡します。Passportはユーザーをreq.userとして処理し、以降のリクエストで利用できるようにします。ユーザーが見つからない場合はPassportにユーザーを渡さないため、認証が失敗したものと判断します。
ユーザーとリクエストハンドラを紐づけし、認証が必要なリクエストが成功しました。
// src/index.js
app.get('/api/secure',
// This request must be authenticated using a JWT, or else we will fail
passport.authenticate(['jwt'], { session: false }),
(req, res) => {
res.send('Secure response from ' + JSON.stringify(req.user));
}
);
3行目はPassportにリクエストを処理させるおまじないで、設定済みの「jwt」ストラテジーを実行します。成功するとリクエストを発行します。
node src/index.jsでアプリケーションを実行し、リソースにアクセスを試みると、実際の動作を確認できます。
// src/index.js
app.get('/api/secure',
// This request must be authenticated using a JWT, or else we will fail
passport.authenticate(['jwt'], { session: false }),
(req, res) => {
res.send('Secure response from ' + JSON.stringify(req.user));
}
);
Authorizationヘッダーになにも渡していないので、処理に失敗します。有効なAuthorizationヘッダーを渡せば、正常なレスポンスを受け取ります。
$ curl -v http://localhost:3000/api/secure -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTczNDAzNzgsImV4cCI6MTQ5NzM0Mzk3OCwiYXVkIjoic29jaWFsLWxvZ2lucy1zcGEiLCJpc3MiOiJzb2NpYWwtbG9naW5zLXNwYSIsInN1YiI6IjAifQ.XlVnG59dX-SykXTJqCmvz_ALvzPW-yGZKOJEGFZ5KUs"
> GET /api/secure HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.51.0
> Accept: */*
> Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTczNDAzNzgsImV4cCI6MTQ5NzM0Mzk3OCwiYXVkIjoic29jaWFsLWxvZ2lucy1zcGEiLCJpc3MiOiJzb2NpYWwtbG9naW5zLXNwYSIsInN1YiI6IjAifQ.XlVnG59dX-SykXTJqCmvz_ALvzPW-yGZKOJEGFZ5KUs
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 60
< ETag: W/"3c-2im1YD4hSDFtwS8eVcEUzt3l5XQ"
< Date: Tue, 13 Jun 2017 07:54:37 GMT
< Connection: keep-alive
<
Secure response from {"id":0,"name":"Graham","providers":[]}
テストを実施するために、https://www.jsonwebtoken.ioのフォームに入力してJWTを手作業で生成しました。使用した「Payload」は以下のとおりです。
{
"iat": 1497340378, // Tuesday, 13 June 2017 07:52:58 UTC
"exp": 1497343978, // Tuesday, 13 June 2017 08:52:58 UTC
"aud": "social-logins-spa",
"iss": "social-logins-spa",
"sub": "0"
}
「Signing Key」はプロパティの「mySuperSecretKey」を使います。
トークン生成のサポート
有効なトークンでリソースにアクセスできるようになったので、次はトークンを実際に生成します。
jsonwebtokenモジュールを使って適切な情報を格納したJWTを作成し、上で使ったものと同じキーで署名します。
// src/token.js
const jwt = require('jsonwebtoken');
const config = require('./config');
// Generate an Access Token for the given User ID
function generateAccessToken(userId) {
// How long will the token be valid for
const expiresIn = '1 hour';
// Which service issued the token
const issuer = config.get('authentication.token.issuer');
// Which service is the token intended for
const audience = config.get('authentication.token.audience');
// The signing key for signing the token
const secret = config.get('authentication.token.secret');
const token = jwt.sign({}, secret, {
expiresIn: expiresIn,
audience: audience,
issuer: issuer,
subject: userId.toString()
});
return token;
}
JWTを生成するときは、audience、issuer、secretをプロパティの設定値と同じ値にする必要があります。JWTの期限は1時間に指定します。アプリケーションにとって適切だと考えられる任意の期間を設定します。簡単に変更できるようにプロパティファイルから読み出すこともできます。
今回はJWT IDを指定していませんが、指定すれば固有のIDを持つトークン(例:UUIDを使ったトークン)を生成できます。無効にしたトークンのID一覧をデータストアに保存して、PassportのストラテジーにおいてJWTを処理するときにそのJWT IDがリストにないことをチェックできます。
ソーシャルログインプロバイダー
トークンを生成できたので、次はユーザーが実際にログインするための方法を実装します。ここでソーシャルログインプロバイダーの出番です。ユーザーをソーシャルログインプロバイダーにリダイレクトさせる機能を追加し、ログインに成功した場合はJWTトークンを生成してブラウザーのJavaScriptエンジンに渡し、以降のリクエストで使います。
必要なピースはほぼ揃っているので、つなぎ合わせるだけです。
Passportでソーシャルログインプロバイダーを使う手順は2つに分かれます。第一に、プラグインでPassportに設定を追加し、ソーシャルログインプロバイダーを利用します。第二に、認証を始めるためにユーザーを移動させ、認証が成功したときにユーザーをリダイレクトさせるためのExpressのルートを用意します。
これらのURL(ルート)をブラウザーの子ウィンドウで開きます。ウィンドウは完了後に閉じます。開いたときにJavaScriptのメソッドを呼び出します。この方法はユーザーにログイン処理を意識させません。認証情報を入力するウィンドウが表示される程度で、設定によってはログインに完了したこと以外はユーザーから見えません。
ブラウザーはポップアップウィンドウのビューと、メインウィンドウの2つです。ビューを処理するJavaScriptはフレームワークを使えば簡単に作成できますが、今回は単純さを考慮して素のJavaScriptを使います。
JavaScriptのメインページは以下の通りです。
// src/public/index.html
let accessToken;
function authenticate(provider) {
window.authenticateCallback = function(token) {
accessToken = token;
};
window.open('/api/authentication/' + provider + '/start');
}
アクセストークンを格納するグローバル関数オブジェクト(authenticateCallback)をwindowに登録し、認証を開始するためのルートであるapi/authentication/{provider}/startにアクセスします。
この関数は認証を開始する方法に合わせて任意の方法で起動できます。通常はヘッダーエリアに設置したログインリンクをクリックしたときに起動しますが、詳細はアプリケーションにより変わります。
2つ目のパートは認証の成功時に描画されるビューです。今回はMustacheを使用していますが、好きなビュー技術で構いません。
<!-- src/public/authenticated.html -->
<!DOCTYPE html>
<html>
<head>
<title>Authenticated</title>
</head>
<body>
Authenticated successfully.
<script type="text/javascript">
window.opener.authenticateCallback('{{token}}');
window.close();
</script>
</body>
</html>
JavaScriptは、ウィンドウのオープナー(つまりアプリケーションのメインウィンドウ)のauthenticateCallbackメソッドを呼び出し、閉じているだけです。
JWTトークンをアプリケーションのメインウィンドウで任意の目的に利用できるようになりました。
Google認証の実装
Google認証を実装するにはpassport-google-oauthモジュールを使います。以下の3つの情報が必要です。
- クライアントID
- クライアントシークレット
- リダイレクトURL
クライアントIDとクライアントシークレットはGoogle Developer Consoleに登録すると入手できます。リダイレクトURLはアプリケーション内のURLで、ユーザーがGoogleの認証情報を使ってサインインしたあとに戻ってくる場所です。リダイレクトURLはアプリケーションをどこに、どのようにデプロイするかによって異なりますが、今回はとりあえずハードコードします。
Google認証のためのPassport設定は以下の通りです。
// src/authentication/google.js
const passport = require('passport');
const passportGoogle = require('passport-google-oauth');
const config = require('../config');
const users = require('../users');
const passportConfig = {
clientID: config.get('authentication.google.clientId'),
clientSecret: config.get('authentication.google.clientSecret'),
callbackURL: 'http://localhost:3000/api/authentication/google/redirect'
};
if (passportConfig.clientID) {
passport.use(new passportGoogle.OAuth2Strategy(passportConfig, function (request, accessToken, refreshToken, profile, done) {
// See if this user already exists
let user = users.getUserByExternalId('google', profile.id);
if (!user) {
// They don't, so register them
user = users.createUser(profile.displayName, 'google', profile.id);
}
return done(null, user);
}));
}
ユーザーが認証を成功させたあとにリダイレクトされて戻ってくるのに合わせて、Googleのシステム内のユーザーIDと、ユーザーのプロファイル情報の一部が提供されます。このユーザーが以前ログインしたことがあるかを確認します。
ログインしたことがあればユーザーの記録を取得し処理を完了します。ログインしたことがなければアカウントを登録し、以降の処理で利用します。このメカニズムのおかげでユーザーは初回ログイン時におけるユーザー登録を意識する必要がないのです。ここでは触れませんが、こうしたトランスペアレントな(利用者にその存在が意識されない)ユーザー登録を実現する方法はいろいろあります。
次に、ログインを管理するためのルートハンドラーをセットアップします。
// src/index.js
function generateUserToken(req, res) {
const accessToken = token.generateAccessToken(req.user.id);
res.render('authenticated.html', {
token: accessToken
});
}
app.get('/api/authentication/google/start',
passport.authenticate('google', { session: false, scope: ['openid', 'profile', 'email'] }));
app.get('/api/authentication/google/redirect',
passport.authenticate('google', { session: false }),
generateUserToken);
ルート/api/authentication/google/startと/api/authentication/gogle/redirectですが、前述のとおり/startはアプリケーション側から開くURLで、/redirectはログインの成功時にGoogleがユーザーをリダイレクトするURLです。上記の認証完了ビューを開いて生成されたJWTをビューに渡します。
Facebook認証の実装
最初のソーシャルログインプロバイダーを用意できたので、2つ目を追加します。Facebook用にpassport-facebookモジュールを使います。
このモジュールはGoogleモジュールとほとんど同じ動作をし、必要なプロパティやセットアップ方法も同じでが、URL構造が異なります。
Facebook認証を設定するには、Google認証のときと同じようにクライアントID、クライアントシークレット、リダイレクトURLが必要です。
クライアントIDとクライアントシークレット(FacebookではアプリID、アプリシークレットと呼ばれます)はFacebook Developer ConsoleでFacebookアプリケーションを作成すると入手できます。
アプリケーションに「Facebook Login」プロダクトを追加します。Facebook認証のためのPassportの設定は以下の通りです。
// src/authentication/facebook.js
const passport = require('passport');
const passportFacebook = require('passport-facebook');
const config = require('../config');
const users = require('../users');
const passportConfig = {
clientID: config.get('authentication.facebook.clientId'),
clientSecret: config.get('authentication.facebook.clientSecret'),
callbackURL: 'http://localhost:3000/api/authentication/facebook/redirect'
};
if (passportConfig.clientID) {
passport.use(new passportFacebook.Strategy(passportConfig, function (accessToken, refreshToken, profile, done) {
let user = users.getUserByExternalId('facebook', profile.id);
if (!user) {
user = users.createUser(profile.displayName, 'facebook', profile.id);
}
return done(null, user);
}));
}
「facebook」の文字だけが異なりますがGoogleとほぼ同じです。以下に示すように、URLルートも似ています。
// src/index.js
app.get('/api/authentication/facebook/start',
passport.authenticate('facebook', { session: false }));
app.get('/api/authentication/facebook/redirect',
passport.authenticate('facebook', { session: false }),
generateUserToken);
スコープはデフォルト設定で十分です。スコープを指定した場合、GoogleとFacebookの設定はほぼ同じになります。
最後に
ソーシャルログインプロバイダーを利用すると、ユーザーのログインや登録機能を簡単に、短時間で追加できます。ユーザーをソーシャルログインプロバイダーにリダイレクトし、さらにアプリケーションに戻すという処理は、従来型のアプリケーションでは簡単ですが、シングルアプリケーションと組み合わせるのは意外と大変です。
本記事ではソーシャルログインプロバイダーをシングルアプリケーションと組み合わせる方法について説明しました。みなさんが将来利用したいと考えているプロバイダーにも簡単に応用できるものであると信じています。
Passportにはさまざまなプロバイダーと連携するためのモジュールが多く用意されているので、目的に合ったものが見つかるはずです。これらのモジュールは本記事で紹介したGoogleやFacebookの認証と同じ方法で利用できます。
本記事はJames Kolceが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:Add Social Login via Google & Facebook to Your Single-page App)
[翻訳:薮田佳佑/編集:Livit]