[Express] Passport.js 자격 확인

2023. 9. 17. 23:49공부 중/Node.js

0. 출처

 

 

passport.js 자격확인 - 생활코딩

수업소개 전송된 정보가 유효한지 여부를 파악하는 방법에 대한 내용입니다. 강의 1 소스코드 변경사항 강의 2 소스코드 변경사항 

opentutorials.org

 

passport-local

Local username and password authentication strategy for Passport.

www.passportjs.org

 

[Node.js] Passport (로컬 로그인 with session)

Passport란? Passport is authentication middleware for Node.js. Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. A comprehensive set of strategies support authentication using a username and pas

dc-choi.tistory.com

 


1. 자격확인

//routes/auth.js
...
const authData = {
    email: 'email',
    password: 'password',
    nickname: 'tiredI'
}

...
router.post('/login', (req,res,next)=>{
    let post = req.body;
    let email = post.email;
    let password = post.pwd;
    if(email == authData.email && password == authData.password){
      req.session.is_logined = true;
      req.session.nickname = authData.nickname;
      req.session.save((err)=>{
        if(err) console.log(err);
        //res.redirect('/');
        res.send(`<script>window.alert("삑! ${authData.nickname}님 환영합니다.");window.location.href = '/';</script>`);
      });
    }else{
      res.send(`<script>window.alert("삐빅! 틀렸습니다. 다시 시도하세요");window.location.href = '/auth/login';</script>`);
    }
});

기존에 로그인 요청을 처리하는 코드다.

 

해당 부분을 passport를 통해서 처리하도록 수정한다.

 


2. passport.authenticate()

 

가. authenticate()

authenticate(
    strategy: string | string[] | Strategy,
    callback?: AuthenticateCallback | ((...args: any[]) => any),
): AuthenticateRet;

authenticate(
    strategy: string | string[] | Strategy,
    options: AuthenticateOptions,
    callback?: AuthenticateCallback | ((...args: any[]) => any),
): AuthenticateRet;

passport.authenticate()의 reference다.

 

우선 URL에 따라서 알맞은 로그인 strategy를 선택한다. (string 혹은 string[])

 

로그인 요청의 결과가 성공인지 실패인지에 따라서 추가적인 동작을 수행한다.

 

추가적인 동작은 옵션이나 콜백으로 선택할 수 있다.

 


나. AuthenticateOptions

interface AuthenticateOptions {
    authInfo?: boolean | undefined;
    /**
     * Assign the object provided by the verify callback to given property.
     */
    assignProperty?: string | undefined;
    /**
     * True to flash failure messages
     * or a string to use as a flash message for failures
     * (overrides any from the strategy itself).
     */
    failureFlash?: string | boolean | undefined;
    /**
     * True to store failure message in `req.session.messages`,
     * or a string to use as override message for failure.
     */
    failureMessage?: boolean | string | undefined;
    /**
     * After failed login, redirect to given URL.
     */
    failureRedirect?: string | undefined;
    failWithError?: boolean | undefined;
    keepSessionInfo?: boolean | undefined;
    /**
     * Save login state in session, defaults to `true`.
     */
    session?: boolean | undefined;
    scope?: string | string[] | undefined;
    /**
     * True to flash success messages
     * or a string to use as a flash message for success
     * (overrides any from the strategy itself).
     */
    successFlash?: string | boolean | undefined;
    /**
     * True to store success message in `req.session.messages`,
     * or a string to use as override message for success.
     */
    successMessage?: boolean | string | undefined;
    /**
     * After successful login, redirect to given URL.
     */
    successRedirect?: string | undefined;
    successReturnToOrRedirect?: string | undefined;
    state?: string | undefined;
    /**
     * Pause the request stream before deserializing the user
     * object from the session.  Defaults to `false`.  Should
     * be set to `true` in cases where middleware consuming the
     * request body is configured after passport and the
     * deserializeUser method is asynchronous.
     */
    pauseStream?: boolean | undefined;
    /**
     * Determines what property on `req`
     * will be set to the authenticated user object.
     * Default `'user'`.
     */
    userProperty?: string | undefined;
    passReqToCallback?: boolean | undefined;
    prompt?: string | undefined;
}

아래는 AuthenticateOptions 인터페이스의 속성들을 표로 정리한 것.

 

 


다. 적용

//routes/auth.js
...
router.post('/login',
  passport.authenticate('local', {
    successRedirect: '/',
    failureRedirect: '/login'
  })
);

성공 시 ‘/’로 리다이렉션, 실패 시 ‘/login’으로 리다이렉션 한다.

 


2. Verify callback

 

로그인에 성공했는지 실패했는지 검증하는 콜백 함수다.

 

 

The LocalStrategy constructor takes a function as an argument. This function is known as a verify function, and is a common pattern in many strategies. When authenticating a request, a strategy parses the credential contained in the request. A verify function is then called, which is responsible for determining the user to which that credential belongs. This allows data access to be delegated to the application.

 

 

인증의 성공과 실패를 판단하는 verify callbackLocalStrategy의 생성자에 인자로 위치한다.

 

다른 strategy에서도 많이 사용되는 패턴이다.

 

passport.use(new LocalStrategy(
    ...
));

LocalStrategy 객체를 생성한다.

 

이때 생성자는 다음과 같다.

 


가. Strategy의 생성자

 

declare class Strategy extends PassportStrategy {
    constructor(options: IStrategyOptionsWithRequest, verify: VerifyFunctionWithRequest);
    constructor(options: IStrategyOptions, verify: VerifyFunction);
    constructor(verify: VerifyFunction);

    name: string;
}

옵션을 선택할 수 있고 검증을 위한 verify function을 추가해야 한다.

 

IStrategyOptionsWithRequestIStrategyOptionsWithRequest의 차이는 아래에서 설명한다.(2-다 참고)

 


나. 생성 옵션

interface IStrategyOptions {
    usernameField?: string | undefined;
    passwordField?: string | undefined;
    session?: boolean | undefined;
    passReqToCallback?: false | undefined;
}

interface IStrategyOptionsWithRequest {
    usernameField?: string | undefined;
    passwordField?: string | undefined;
    session?: boolean | undefined;
    passReqToCallback: true;
}

옵션에 대하여 살펴보면

  • session : 로그인 상태를 세션에 저장할지 여부를 결정합니다. 기본값은 true.
  • passReqToCallback : verify callback에 req 객체를 전달할지 여부

 

<form action="/auth/login" method="post">
	<p>
        <div>
            <label>
                <b>Email</b></br>
                <input type="text" name="email" placeholder="email" required>
            </label>
        </div>
        <div>
            <label>
                <b>Password</b></br>
                <input type="password" name="pwd" placeholder="password" required>
            </label>
        </div>
    </p>
	<input type="submit" value="login">
</form>
  • usernameField
    : HTTP POST 입력 중에 앞으로 passport에서 usernameField로 사용할 값을 선택한다.
    : 위에서 name=”email"에 해당한다.
    : 기본값은 "username"이다.

 

  • passwordField
    : HTTP POST 입력 중에 앞으로 passport에서 passwordField로 사용할 값을 선택한다.
    : 위에서 name=”pwd"에 해당한다.
    : 기본값은 "password"이다.

 


다. Verify callback

interface IVerifyOptions {
    message: string;
}

interface VerifyFunctionWithRequest {
    (
        req: express.Request,
        username: string,
        password: string,
        done: (error: any, user?: Express.User | false, options?: IVerifyOptions) => void,
    ): void;
}

interface VerifyFunction {
    (
        username: string,
        password: string,
        done: (error: any, user?: Express.User | false, options?: IVerifyOptions) => void,
    ): void;
}

VerifyFunctionWithRequestVerifyFunction의 차이는 req: express.Request의 유무다.

 

VerifyFunctionWithRequestexpress.Request 객체 (req)와 함께 호출된다.

 

이렇게 하면 콜백 내에서 req 객체를 사용하여 추가 정보를 가져올 수 있다.

 

Verify callback들은 username, usernamedone을 매개변수로 가지고 있는 함수여야 한다.

  • username, username
    : 전달받은 아이디, 패스워드. string이다.
  • done
    : verify callback에서 결과적으로 반환해야 하는 콜백함수다.
    : 검증의 결과가 담겨있다. 인증의 결과를 Passport 프레임워크에 알린다.

 


라. done()

done: (error: any, user?: Express.User | false, options?: IVerifyOptions) => void,

 

 

아래는 일반적으로 사용하는 done 함수 호출 방식을 표로 정리한 것입니다:

 

 


마. 적용

//routes/auth.js
...
const authData = {
    email: 'email',
    password: 'password',
    nickname: 'tiredI'
}

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'pwd'
  },
  (email, password, done)=>{
    if(email == null || email != authData){
      return done(null, false, {
        message: "삐삑! 잘못된 이메일입니다. 다시 시도하세요"
      });
    }
    if(password == null || password != authData.password){
      return done(null, false, {
        message: "비삑! 잘못된 패스워드입니다 다시 시도하세요"
      });
    }
    return done(null, authData,{
      message: "삑! ${authData.nickname}님 환영합니다."
    });
  }
));

아직 session에 대한 부분이 미완성되어서 정상적으로 동작하진 않음.

 


 

예상해서 그린 다이어그램입니다. 불확실한 정보가 포함되어 있을 수 있습니다.

 

 

'공부 중 > Node.js' 카테고리의 다른 글

[Express] Passport.js 시작하기  (0) 2023.09.17
[Express] Passport.js이란?  (0) 2023.09.17
[Express] Session으로 인증 구현하기_2  (0) 2023.08.22
[Express] Session으로 인증 구현하기_1  (0) 2023.08.22
[Express] Session이란?  (0) 2023.08.19