Carrot征服市場ノート[8]-「認証ページ」
16220 ワード
このページでは、ユーザー認証のすべての機能が実装され、ログイン時に一度にパスワードを入力してログインします.そのため,token活用,T柳,SendGridなどが含まれている.
phoe numberまたはemail adressを使用してアカウントを作成するロジックを実装します.
pages/api/users/enter.tsxは通常クエリーです.検索するデータにfindUniqueクエリーがない場合は、createクエリーを作成するためのコードを作成できます.これは長いコードですが、ここで使用するupsertクエリーはクエリーです.MySQLのUNIQE KEY値に重複値がない場合は、Updataを実行し、重複する場合はInsertを実行できます.コードを簡略化できます.使用する場合は、3つのオプション(create、where、update)を明記する必要があります.また、constペイロード=phone?{ phone: +phone } : { email }; これは3つの演算で、コードを簡略化するために必要な複数の値を条件付きで使用できます.
prisma/schema.prisma
ConnectOrCreateを使用すると、既存のユーザーに新しいtokenを接続できます.ユーザーがいない場合は、新しいユーザーアカウントIDとtokenを作成できます.whereとcreateを記入する必要があります.
connectOrCreateを使用してtoken接続だけでなく、ユーザーアカウントIDも検索し、ない場合は新しい(tokenとともに)を作成します.
libs/server/withHandler.ts
pages/api/users/enter.tsxでres.status(200)を返します.end(); このように、httpステータスコードを返すのではなく、req、res変数をboolean typeとして指定することで、上記のコードのように使用できます.
この項目では,ログインはユーザが入力したPhoneまたはemailを介してワンタイムパスワードを提供するとともに,ログインロジックを提供し,TwiioはSMSを送信する機能を提供することができる.その他、WebRTC、ビデオ電話なども利用できます.
Twilioアカウントを作成し、Account SIDとAUTH TOKENを使用します.envファイルを入れます. 「メッセージサービス」タブでsender poolフェーズでtryit out-get setupページに移動し、setupボタンを押すと、米国の電話番号が表示されます. Trialアカウントなので、送った番号は変えられません.試用料は毎月1ドルです. メッセージングサービスは、MSG SIDに再アクセスする.envファイルに保存します.
pages/api/users/enter.tsx
これは、ワンタイムパスワードtokenをSMSではなく、SendGrid(bytwiio)を介してユーザに電子メールアドレスを送信する方法である.
会員加入後、API KEYを作成する.envファイル に挿入
pages/api/users/enter.tsx
SMSもemailも、現在は試用目的で、to:一部を私の携帯電話番号、電子メールアドレスに設定しますが、後でユーザー情報を受信して処理します.注意しなければならないのは、この部分を処理して、必ず費用を最低限のmoneyに下げなければなりません...TESTプロセスでコメント処理を行います.
まず、Type Scriptでは、Genericは再利用可能な構成部品を作成するために使用され、異なるタイプに役立つ構成部品を作成することができます.ここでTはType parameterと呼ばれ、JENARICを宣言する際に慣用される識別子である.
libs/client/useMutation.tsx
このプロジェクトでは、UserMutionStateに宣言されたデータ型をobjectとして指定しましたが、pages/enterです.tsxファイルでは、Booleanタイプを使用してデータが存在するかどうかを表すためにGenericを使用しています.また、データを使用するすべてのオブジェクトもTを宣言する必要があります.
Tokenがemailまたはphone#に送信された場合、UIにはパスワードtokenを入力してログインする画面が必要です.
pages/enter.tsx
Tokenに対してuseMutation hookを再使用し、同じ変数名を別の同じ変数名に書き込む場合は、タイプ名を異なる変数として指定できます.
pages/api/users/confirm.tsx
今回は、ironセッションを使用してユーザーの認証を行います.サーバlessが成立するのは,ユーザがログインする際に使用するトークンが暗号化され,クッキー方式で格納・ロードされるためである.操作手順は、ペイロード(トークン番号)を暗号化する->暗号化されたペイロードをクッキーとしてユーザに送信する->受信したクッキーを暗号化する->現在のページにアクセスするユーザのidにアクセス性を付与する方式で実現される.
ちょっと待って.
JWT(Json Web Token)は、ユーザIDを持つオブジェクトに署名し、署名とともにユーザにトークンを送信するものであり、JWTはトークン中の情報を識別することができ、セキュリティ上少し脆弱である可能性がある.また、セッションのバックエンドを構築する必要があります.しかしながら、iron sessionを使用すると、これらの欠点を保護し、認証ロジックを実現することができる.
インストーラのマニュアルは次のとおりです.npm i iron-session Ironセッション使用方法 使用したい関数をiron session helper関数で包みます. ironセッションに暗号化パスワードを設定します(長い複雑なパスワードを使用します). より前に受信したTokenをセッションに保存します. クッキーで暗号化されたtokenがユーザ情報と同じである場合、ログインは完了する.
pages/api/users/confirm.tsx
pages/api/users/me.tsx withHandler関数をironsession withIronSessionApiRouteで包むとres.sessionにアクセスできます.このプロジェクトでは、すべてのapiが同じサーバではなく単独で実行されます.したがって、apiごとにironセッションをカプセル化します. 以下に示すように、 ironセッション設定を追加する必要があります.次に、promission voidまたはanyをwithHandlerに返さなければなりません.
tokenのような関係のあるモデルを削除する必要がある場合は、次のコードのようにCascadeを追加できます.これはparent recordを削除するとchild recordも削除されることを意味します.Setnullを使用する場合はtokenを使用できますが、nullを使用して置き換えることができます. prisma/schema.prisma
phone#の場合、国番号も一緒に入力されるため、ユーザーモデルタイプはBigIntまたはStringに処理する必要があります.
npx prisma dbpush:アーキテクチャの更新時に提供
1. Accounts logic
phoe numberまたはemail adressを使用してアカウントを作成するロジックを実装します.
pages/api/users/enter.tsx
import { NextApiRequest, NextApiResponse } from "next";
import withHandler from "@libs/server/withHandler";
import client from "@libs/server/client";
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { phone, email } = req.body; // user 가 입렵후 submit 한 phone 또는 email 정보를 req.body 로 가져옴.
const inputInfo = phone ? { phone: +phone } : { email }; //es6 삼항 연산 !!
const user = await client.user.upsert({
where: {
...inputInfo,
},
create: {
name: "Anonymous", // name is required in user table so, init as Anonymous
...inputInfo,
},
update: {},
});
console.log(user);
return res.status(200).end();
}
export default withHandler("POST", handler);
※ Point
2. Token Logic
prisma/schema.prisma
model User {
// ...
tokens Token[] // 여러개[]의 Token 을 User 에 담음.
}
model Token {
id Int @id @default(autoincrement())
payload String @unique
user User @relation(fields: [userId], references: [id])
// User 테이블에 Token 테이블에 관계성을 Token의 userId를 필드로 와 User의 id를 참조로 지정.
userId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
pages/api/users/enter.tsx async function handler(req: NextApiRequest, res: NextApiResponse) {
const { phone, email } = req.body;
const user = phone ? { phone: +phone } : { email };
const payload = Math.floor(100000 + Math.random() * 900000) + "";
// payload 는 User 모델 내에서 unique한 값이어야 되어, 6자리의 random 숫자를 생성하고 + "" 의 의미는 string 으로 변환.
const token = await client.token.create({
data: {
payload,
user: {
connectOrCreate: { // command 키를 누르고 data 를 클릭해보면, user 필드를 꼭 넣어주야 한다는 것을 알수 있는데 한층 더 가보게되면 3가지 옵션이 나오는데 그중, connectOrCreate 사용 하여 modle 들을 연결하고 생성 하겠다는 의미
where: {
...user,
},
create: {
name: "Anonymous",
...user,
},
},
},
},
});
console.log(token);
※ Point
ConnectOrCreateを使用すると、既存のユーザーに新しいtokenを接続できます.ユーザーがいない場合は、新しいユーザーアカウントIDとtokenを作成できます.whereとcreateを記入する必要があります.
connectOrCreateを使用してtoken接続だけでなく、ユーザーアカウントIDも検索し、ない場合は新しい(tokenとともに)を作成します.
3.req、resブール化
libs/server/withHandler.ts
export interface ResponseType {
ok: boolean;
[key: string]: any;
}
pages/api/users/enter.tsx import withHandler, { ResponseType } from "@libs/server/withHandler";
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { phone, email } = req.body;
const user = phone ? { phone: +phone } : email ? { email } : null;
if (!user) return res.status(400).json({ ok: false });
// 만약 phone 이나 email 정보 없이 user 가 form 을 제출 하게되면 400 에러 코드를 보낸다.
return res.json({
ok: true,
});
}
※ Point
pages/api/users/enter.tsxでres.status(200)を返します.end(); このように、httpステータスコードを返すのではなく、req、res変数をboolean typeとして指定することで、上記のコードのように使用できます.
4. Twilio
この項目では,ログインはユーザが入力したPhoneまたはemailを介してワンタイムパスワードを提供するとともに,ログインロジックを提供し,TwiioはSMSを送信する機能を提供することができる.その他、WebRTC、ビデオ電話なども利用できます.
Setup
4-1 Twiio SMSメッセージの送信方法
pages/api/users/enter.tsx
import twilio from "twilio";
const twilioClient = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
// .env 파일에있는 Twilio SID 와 TOKEN 을 받아옴.
if (phone) {
const message = await twilioClient.messages.create({
messagingServiceSid: process.env.TWILIO_MSID,
to: process.env.MY_PHONE!,
body: `Your login token is ${payload}.`,
});
console.log(message);
}
4-2 Twiioメールの送信方法
これは、ワンタイムパスワードtokenをSMSではなく、SendGrid(bytwiio)を介してユーザに電子メールアドレスを送信する方法である.
Setup
pages/api/users/enter.tsx
import mail from "@sendgrid/mail";
mail.setApiKey(process.env.SENDGRID_KEY!);
else if (email) {
const email = await mail.send({
from: "Email 주소 입력",
to: "Email 주소 입력",
subject: "Your Carrot Market Verification Email",
text: `Your token is ${payload}`,
html: `<strong>Your token is ${payload}</strong>`, //만약을 위해 html 도 같이 보내준다.
});
console.log(email);
}
※ Point
SMSもemailも、現在は試用目的で、to:一部を私の携帯電話番号、電子メールアドレスに設定しますが、後でユーザー情報を受信して処理します.注意しなければならないのは、この部分を処理して、必ず費用を最低限のmoneyに下げなければなりません...TESTプロセスでコメント処理を行います.
5 Token UI
5-1 Type Script Genericを使用します。
まず、Type Scriptでは、Genericは再利用可能な構成部品を作成するために使用され、異なるタイプに役立つ構成部品を作成することができます.ここでTはType parameterと呼ばれ、JENARICを宣言する際に慣用される識別子である.
libs/client/useMutation.tsx
import { useState } from "react";
interface UseMutationState<T> {
loading: boolean;
data?: T; // 이전엔 data의 type 은 object이였다.
error?: object;
}
type UseMutationResult<T> = [(data: any) => void, UseMutationState<T>];
// 이 코드 또한 <T>를 선언해준다
export default function useMutation<T = any>( //첫번째, <T> 선언 해주고 UseMutationState으로 보냄.
url: string
): UseMutationResult<T> // 두번째, <T>를 다시 UseMutationResult 넣어주고 {
const [state, setSate] = useState<UseMutationState<T>>({
loading: false,
data: undefined,
error: undefined,
});
※ Point 5-1
このプロジェクトでは、UserMutionStateに宣言されたデータ型をobjectとして指定しましたが、pages/enterです.tsxファイルでは、Booleanタイプを使用してデータが存在するかどうかを表すためにGenericを使用しています.また、データを使用するすべてのオブジェクトもTを宣言する必要があります.
5-2 Token入力ページを作成します。
Tokenがemailまたはphone#に送信された場合、UIにはパスワードtokenを入力してログインする画面が必要です.
pages/enter.tsx
interface TokenForm { // token을 위한 type 선언
token: string;
}
interface MutationResult {
ok: boolean;
}
const Enter: NextPage = () => {
const [enter, { loading, data, error }] =
useMutation<MutationResult>("/api/users/enter");
const [confirmToken, { loading: tokenLoading, data: tokenData }] =
useMutation<MutationResult>("/api/users/confirm");
// token을 위한 새로운 mutation hook. 이름을 바꿀쑤 있는것은 useMutation 이 배열을 리턴하기 때문이다.
const { register, handleSubmit, reset } = useForm<EnterForm>();
const { register: tokenRegister, handleSubmit: tokenHandleSubmit } =
useForm<TokenForm>();
// register과 handleSubmit은 이미 사용 되어 지고 있는 이름이기 때문에 새로운 type을 적용해 다른 변수로 취급하게 한다.
const onTokenValid = (validForm: TokenForm) => {
if (tokenLoading) return;
confirmToken(validForm);
};
return (
{data?.ok ? ( // 이 부분에서 .ok? 부분을 boolean 으로 하고 싶기 때문에 boolean type 으로 선언되어 있는 MutationResult객체 타입을 useMutation 에 적용 한 것이다.
<form
onSubmit={tokenHandleSubmit(onTokenValid)}
className="flex flex-col mt-8 space-y-4"
>
<Input
register={tokenRegister("token", {
required: true,
})}
name="token"
label="Confirmation Token"
type="number"
required
/>
<Button text={tokenLoading ? "Loading" : "Confirm Token"} />
</form>
) : (
<>
// 기존 enter 페이지의 UI
</>
)
※ Point 5-2
Tokenに対してuseMutation hookを再使用し、同じ変数名を別の同じ変数名に書き込む場合は、タイプ名を異なる変数として指定できます.
Tokenチェックの作成
pages/api/users/confirm.tsx
import { NextApiRequest, NextApiResponse } from "next";
import withHandler, { ResponseType } from "@libs/server/withHandler";
import client from "@libs/server/client";
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { token } = req.body; //req.body 에서 token 을 받아 온다.
console.log(token);
res.status(200).end(); // 전송 ok
}
export default withHandler("POST", handler);
6. Serverless Sessions
今回は、ironセッションを使用してユーザーの認証を行います.サーバlessが成立するのは,ユーザがログインする際に使用するトークンが暗号化され,クッキー方式で格納・ロードされるためである.操作手順は、ペイロード(トークン番号)を暗号化する->暗号化されたペイロードをクッキーとしてユーザに送信する->受信したクッキーを暗号化する->現在のページにアクセスするユーザのidにアクセス性を付与する方式で実現される.
ちょっと待って.
JWT(Json Web Token)は、ユーザIDを持つオブジェクトに署名し、署名とともにユーザにトークンを送信するものであり、JWTはトークン中の情報を識別することができ、セキュリティ上少し脆弱である可能性がある.また、セッションのバックエンドを構築する必要があります.しかしながら、iron sessionを使用すると、これらの欠点を保護し、認証ロジックを実現することができる.
インストーラのマニュアルは次のとおりです.npm i iron-session
Tokenデータセッションの読み込み(POST)
pages/api/users/confirm.tsx
import { withIronSessionApiRoute } from "iron-session/next";
declare module "iron-session" { // 아래 res.session.user 에서 모듈의 정의를 찾을수 없다는 error가 생긴다.
이런 경우엔, declare를 선언 하여 해당 변수가 존재 한다는것을 알려주어야 한다.
interface IronSessionData {
user?: {
id: number;
};
}
}
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { token } = req.body;
const exists = await client.token.findUnique({
where: {
payload: token, // token 모듈에 payload 필드 값을 저장 한다.
},
});
if (!exists) return res.status(404).end(); // Token 이 없다면 404 에러 코드
req.session.user = { // 토큰이 존재한다면, userId 를 세션에 id를 넣어 생성.
id: exists?.userId,
};
await req.session.save(); // 암호화가 된 세션을 저장
res.status(200).end();
}
export default withIronSessionApiRoute(withHandler("POST", handler), {
cookieName: "carrotsession",
password:
"복잡한 구조의 아무런 번호 넣는곳",
});
Tokenデータ情報の読み込み(GET)
pages/api/users/me.tsx
import { withIronSessionApiRoute } from "iron-session/next";
import { NextApiRequest, NextApiResponse } from "next";
import withHandler, { ResponseType } from "@libs/server/withHandler";
import client from "@libs/server/client";
declare module "iron-session" {
interface IronSessionData {
user?: {
id: number;
};
}
}
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
console.log(req.session.user);
const profile = await client.user.findUnique({
where: { id: req.session.user?.id },
});
res.json({
ok: true,
profile,
});
}
export default withIronSessionApiRoute(withHandler("GET", handler), {
cookieName: "carrotsession",
password:
"9845904809485098594385093840598df;slkgjfdl;gkfsdjg;ldfksjgdsflgjdfklgjdflgjflkgjdgd",
});
※ Point
知っておくべきこと。
Prismaについて知りたいこと
model Token {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
phone#の場合、国番号も一緒に入力されるため、ユーザーモデルタイプはBigIntまたはStringに処理する必要があります.
npx prisma dbpush:アーキテクチャの更新時に提供
Reference
この問題について(Carrot征服市場ノート[8]-「認証ページ」), 我々は、より多くの情報をここで見つけました https://velog.io/@jaylion_2009/Carrot-market-정복-노트-8-Authentication-pageテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol