Files

109 lines
6.3 KiB
Markdown
Raw Permalink Normal View History

---
id: 589a69f5f9fc0f352b528e70
title: ソーシャル認証の実装
challengeType: 2
forumTopicId: 301559
dashedName: implementation-of-social-authentication
---
# --description--
アプリでこの種の認証を行うには、以下のパスに従います。
1. ユーザーは、特定のストラテジー (たとえば、GitHub) を使用して認証を受けるためのルートに自身を送信するボタンまたはリンクをクリックします。
2. ルートは `passport.authenticate('github')` を呼び出し、ユーザーを GitHub へリダイレクトします。
3. ユーザーがたどり着いた GitHub のページで、ユーザーがまだログインしていない場合はログインが許可されます。 次に、アプリからのプロファイルへのアクセスを承認するようユーザーに求めます。
4. ユーザーはアプリの特定のコールバック URL に戻り、ユーザーが承認した場合はプロファイルが返されます。
5. これでユーザーが認証され、アプリでは、プロファイルが返却されたプロファイルかどうかを確認します。そうでない場合はデータベースに保存します。
OAuth を使用したストラテジーでは、少なくとも*クライアント ID* と*クライアント シークレット*が必要です。サービスはこれらを使用して、認証リクエストが誰からのものか、またそれが有効かどうかを確認します。 これらは、認証を実装しようとしている GitHub などのサイトから取得され、アプリに固有のものです。これらの情報は**共有すべきではありません**。したがって、公開リポジトリにアップロードしたり、コード内に直接書き込んだりしないでください。 通常は、それらを `.env` ファイルに保存し、`process.env.GITHUB_CLIENT_ID` などのように参照します。 このチャレンジでは、GitHub ストラテジーを使用します。
GitHub からの*クライアント ID とシークレット*の取得は、「開発者設定」のアカウントプロファイル設定で実行され、その後は「[OAuth アプリケーション](https://github.com/settings/developers)」で実行されます。 「Register a new application (新しいアプリを登録する)」をクリックし、アプリに名前を付け、URL を Replit のホームページに貼り付けます (**プロジェクトコードの URL ではありません**)。最後に、コールバック URL をホームページと同じ URL に貼り付けますが、`/auth/github/callback` を追加します。 ユーザーはここにリダイレクトされ、GitHub で認証された後、処理が行われます。 返された情報を `'GITHUB_CLIENT_ID'` および `'GITHUB_CLIENT_SECRET'` として `.env` ファイルに保存します。
`routes.js` ファイルで、`showRegistration: true` の後に、`showSocialAuth: true` をホームページルートに追加します。 GET リクエストを受け付けるルートを 2 つ作成します。それらは、`/auth/github``/auth/github/callback` です。 1 つ目は、Passport を呼び出して `'github'` を認証するだけです。 2 つ目は、Passport を呼び出して `'github'` を認証した結果失敗し、`/` へリダイレクトした後、リダイレクトが成功した場合は `/profile` へリダイレクトします(前回のプロジェクトと同様です)。
`/auth/github/callback` の内容は、たとえば通常のログイン処理と似たものになります。
```js
app.route('/login')
.post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => {
res.redirect('/profile');
});
```
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/1f7f6f76adb178680246989612bea21e)で確認できます。
# --hints--
ルート `/auth/github` が正確である必要があります。
```js
async (getUserInput) => {
try {
const res = await fetch(getUserInput('url') + '/_api/routes.js');
if (res.ok) {
const data = await res.text();
assert.match(
data.replace(/\s/g, ''),
/passport.authenticate.*?github/g,
'Route auth/github should only call passport.authenticate with github'
);
} else {
throw new Error(res.statusText);
}
const res2 = await fetch(getUserInput('url') + '/_api/app-stack');
if (res2.ok) {
const data2 = JSON.parse(await res2.json());
const dataLayer = data2.find(layer => layer?.route?.path === '/auth/github');
assert.deepInclude(dataLayer?.route, { methods: {get: true}, path: "/auth/github"});
assert.deepInclude(dataLayer?.route?.stack?.[0], {method: "get", name: "authenticate"});
} else {
throw new Error(res2.statusText);
}
} catch (err) {
throw new Error(err);
}
}
```
ルート `/auth/github/callback` が正確である必要があります。
```js
async (getUserInput) => {
try {
const res = await fetch(getUserInput('url') + '/_api/routes.js');
if (res.ok) {
const data = await res.text();
assert.match(
data.replace(/\s/g, ''),
/failureRedirect:("|')\/\1/g,
'Route auth/github/callback should accept a get request and call passport.authenticate for github with a failure redirect to home'
);
} else {
throw new Error(res.statusText);
}
const res2 = await fetch(getUserInput('url') + '/_api/app-stack');
if (res2.ok) {
const data2 = JSON.parse(await res2.json());
const dataLayer = data2.find(layer => layer?.route?.path === '/auth/github/callback');
assert.deepInclude(dataLayer?.route, { methods: {get: true}, path: "/auth/github/callback"});
assert.deepInclude(dataLayer?.route?.stack?.[0], {method: "get", name: "authenticate"});
} else {
throw new Error(res2.statusText);
}
} catch (err) {
throw new Error(err);
}
}
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```