6.3 KiB
id, title, challengeType, forumTopicId, dashedName
id | title | challengeType | forumTopicId | dashedName |
---|---|---|---|---|
589a69f5f9fc0f352b528e70 | ソーシャル認証の実装 | 2 | 301559 | implementation-of-social-authentication |
--description--
アプリでこの種の認証を行うには、以下のパスに従います。
- ユーザーは、特定のストラテジー (たとえば、GitHub) を使用して認証を受けるためのルートに自身を送信するボタンまたはリンクをクリックします。
- ルートは
passport.authenticate('github')
を呼び出し、ユーザーを GitHub へリダイレクトします。 - ユーザーがたどり着いた GitHub のページで、ユーザーがまだログインしていない場合はログインが許可されます。 次に、アプリからのプロファイルへのアクセスを承認するようユーザーに求めます。
- ユーザーはアプリの特定のコールバック URL に戻り、ユーザーが承認した場合はプロファイルが返されます。
- これでユーザーが認証され、アプリでは、プロファイルが返却されたプロファイルかどうかを確認します。そうでない場合はデータベースに保存します。
OAuth を使用したストラテジーでは、少なくともクライアント ID とクライアント シークレットが必要です。サービスはこれらを使用して、認証リクエストが誰からのものか、またそれが有効かどうかを確認します。 これらは、認証を実装しようとしている GitHub などのサイトから取得され、アプリに固有のものです。これらの情報は共有すべきではありません。したがって、公開リポジトリにアップロードしたり、コード内に直接書き込んだりしないでください。 通常は、それらを .env
ファイルに保存し、process.env.GITHUB_CLIENT_ID
などのように参照します。 このチャレンジでは、GitHub ストラテジーを使用します。
GitHub からのクライアント ID とシークレットの取得は、「開発者設定」のアカウントプロファイル設定で実行され、その後は「OAuth アプリケーション」で実行されます。 「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
の内容は、たとえば通常のログイン処理と似たものになります。
app.route('/login')
.post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => {
res.redirect('/profile');
});
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトをこちらで確認できます。
--hints--
ルート /auth/github
が正確である必要があります。
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
が正確である必要があります。
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--
/**
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.
*/