QR코드와 OTP로 인증 구현하기

sirl0·2021년 10월 8일
7

React 표류기

목록 보기
2/2
post-thumbnail

QR코드와 OTP로 로그인을 할 수 있는 환경을 만들어봅니다.

Intro

다른 팀에서 프론트엔드를 하는 동료가 귀찮고 번거로운 일을 얻어와서 끙끙거리고 있길래 언젠가 쓸 것 같아서 예제를 만들어두었다.
귀찮아귀찮아 😖

OTP(One-Time Password)란?

OTP는 무작위 번호약속 알고리즘에 따라 매 시간마다 변경되는 추정 할 수 없는 비밀번호 생성을 이용하는 보안 시스템이라고 한다.

간단하게 말해서 일정시간마다 바뀌는 비밀번호이다.

흔히 은행에서 쓰는 토큰형 OTP를 많이 떠올릴텐데, 그거 맞다.

그리고 최근은 아니지만, 얼마 전에 회사에서 회사 이메일과 Jira 계정을 2단계 OTP를 걸어두라고 해서 Google Authenticator를 15일마다 쓰고있다.
아무튼, 이번에는 Google Authenticator로 로그인을 해보도록 하자!

Getting Started

일단 간단하게 express 서버 하나랑 React 프로젝트를 하나씩 만들어두자.우선 서버쪽부터 시작할건데, 그냥 간단한 테스트용이라 유저인증이 되는 것처럼만 만들어보도록 하자.

1.서버 코드 작성하기

mkdir server
cd server
yarn init -y
yarn add express express-sessions qrcode speakeasy

이후 index.js 를 만들어주고 아래와 같이 코드를 작성하자

const express = require("express");
const app = express();
const port = 3000;
const session = require("express-session");

const speakeasy = require("speakeasy");
const QRCode = require("qrcode");

app.use(
  session({
    secret: "@#@$MYSIGN#@$#$",
    resave: false,
    saveUninitialized: true,
  })
);

app.use(express.json())


app.get("/qr", (req, res) => {
  let result = {}
  const secret = speakeasy.generateSecret({
    length: 10,
    name: "test@test.com",
    algorithm: "sha512",
  });

  sess = req.session;
  sess.secret = secret;

  const url = speakeasy.otpauthURL({
    secret: secret.ascii,
    issuer: "TEST",
    label: "sirl0@naver.com",
    algorithm: "sha512",
    period: 30,
  });

  QRCode.toDataURL(url, function (err, image_data) {
    console.log(image_data); // A data URI for the QR code image
    res.json({
      "img": image_data
    })
  });
});

app.post("/auth", (req, res) => {
  sess = req.session;
  console.log(sess.secret);

  console.log(req.body.token);

  const verified = speakeasy.totp.verify({
    secret: sess.secret.base32,
    encoding: "base32",
    algorithm: "sha512",
    token: req.body.token,
  });

  res.json({
    "loggedIn": verified
  })
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

그냥 테스트용 코드라서 막 작성했는데, 간단하게 요약하자면

  • /qr 로 get 요청을 날리면 secret 값을 세션에 담아서 QR 코드 이미지 데이터 값과 함께 보낸다.

  • /auth 로 token값과 함께 post 요청을 날리면 인증 유무를 확인한 뒤 데이터를 보낸다.

이때 당연한 말이지만, /qr 와 /auth 에서 사용하는 speakeasy의 algorithm 값이 같아야한다.

node index.js

명령어로 express 서버를 실행시켜둔 뒤, 이제 프론트엔드 코드를 작성해보자.

2. 프론트엔드 코드 작성하기

최대한 간단하게 작성하기 위해서 CRA로 React APP을 하나 만들어준 뒤, express 서버와 통신하기 위해 axios 모듈 하나만 설치해준다.

npx create-react-app client
cd client
yarn add axios

이후 src/App.jsx 의 코드를 일부 바꾸어준다.

import React, { useEffect, useState } from "react";
import axios from "axios";
import logo from "./logo.svg";
import "./App.css";

function App() {
  const [value, setValue] = useState("");
  const [qr, setQR] = useState("");

  useEffect(() => {
    axios.get("/qr").then((item) => {
      setQR(item.data.img)
    });
  }, []);

  const handleInput = (e) => {
    e.preventDefault();
    setValue(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    axios.post('/auth', {"token" : value}).then(res => {
      console.log(res)
    })
  };

  return (
    <div className="App">
      <header className="App-header">
        <img src={qr} className="App-logo" alt="logo" />

        <input type="text" onChange={handleInput} />
        <button onClick={handleSubmit}>BUTTON</button>
      </header>
    </div>
  );
}

export default App;

간단하게 렌더링될 때, express 서버로 QR 이미지 데이터를 불러오도록 하고, 이 데이터 값을 img 태그의 src 값에 넣어주면 알아서 이미지를 뚝딱 만들어준다.
그리고 이 QR 코드를 Google Authenticator 앱으로 찍으면 6자리 숫자가 나오게 되는데, 이를 하단 input 에 적어서 button 을 누르면 /auth 주소로 이 값을 보내 인증이 되었는지 안되었는지 확인해볼 수 있다.

아 아마 css를 안바꾸면 QR 코드가 빙글빙글 돌아가고 있을건데(..) 사실 돌아가도 상관없지만.. 불편하다면 css 를 수정해주자.

그럼 끝..!

예제 코드 : https://github.com/JunHyeokHa/otp-client

profile
쉽게 읽을 수 있는 글을 쓰려고 노력하고 있습니다.

0개의 댓글