firebase 를 이용하여 로그인/회원가입등의 API 구현하기

hwisaac·2023년 3월 15일
0

Firebase

목록 보기
1/3

Goal:
1. firebase function 을 이용해서 API를 만듭니다
2. 회원가입과 로그인 기능을 구현하려고 합니다.
3. 회원의 정보는 firstore database 에 저장해야 합니다.
4. JWT 방식이어야 하고, accessToken 의 유효기간은 24시간입니다.

Firebase Functions를 이용하여 API를 만들고 회원가입 및 로그인 기능을 구현하는 방법은 다음과 같습니다. 이 예제에서는 Firestore 데이터베이스를 사용하여 회원 정보를 저장하고 JWT(JSON Web Token)를 사용하여 인증을 처리합니다.

Firebase 프로젝트 설정:

  • Firebase 프로젝트를 생성하거나 기존 프로젝트를 사용하세요.
  • Firebase CLI를 설치하고 로그인하세요.

Cloud Functions를 위한 프로젝트 디렉토리 설정:

  • firebase init functions 명령을 실행하여 함수 프로젝트를 초기화하세요.
  • 생성된 functions 폴더로 이동하세요.

패키지 설치:

  • npm을 사용하여 필요한 패키지를 설치하세요.
npm install --save express cors firebase-admin jsonwebtoken

코드 작성:

코드가 정상 동작하려면 firebase의 기능을 활성화 해야 합니다.

functions/index.js 파일을 열고 다음 코드를 추가하세요.

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const cors = require("cors");
const jwt = require("jsonwebtoken");

admin.initializeApp();
const firestore = admin.firestore();

const app = express();
app.use(cors({ origin: true }));

const SECRET_KEY = "your-secret-key"; // 변경해야 함
const TOKEN_EXPIRATION = "24h";

// 회원가입 API
app.post("/signup", async (req, res) => {
  const { email, password, firstName, lastName } = req.body;

  try {
    const userRecord = await admin.auth().createUser({ email, password });
    const userId = userRecord.uid;

    await firestore.collection("users").doc(userId).set({
      firstName,
      lastName,
      email,
    });

    const token = jwt.sign({ userId }, SECRET_KEY, { expiresIn: TOKEN_EXPIRATION });
    return res.status(201).json({ accessToken: token });
  } catch (error) {
    return res.status(400).json({ error: error.message });
  }
});

// 로그인 API
app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  try {
    const userRecord = await admin.auth().getUserByEmail(email);
    const userId = userRecord.uid;

    const userSnapshot = await firestore.collection("users").doc(userId).get();
    const userData = userSnapshot.data();

    if (!userData) {
      return res.status(404).json({ error: "User not found" });
    }

    // 패스워드 검증은 Firebase의 사용자 관리 기능을 사용하는 것이 좋습니다.
    const token = jwt.sign({ userId }, SECRET_KEY, { expiresIn: TOKEN_EXPIRATION });
    return res.status(200).json({ accessToken: token });
  } catch (error) {
    return res.status(400).json({ error: error.message });
  }
});

exports.api = functions.https.onRequest(app);

jwt.sign() 함수

jwt.sign() 함수는 JSON Web Token(JWT) 라이브러리를 사용하여 JWT를 생성하는 데 사용됩니다. 이 함수는 주어진 payload, secret key, 그리고 옵션을 기반으로 암호화된 토큰을 생성합니다. 생성된 토큰은 클라이언트에게 전달되어 인증을 위해 사용됩니다.

const token = jwt.sign({ userId }, SECRET_KEY, { expiresIn: TOKEN_EXPIRATION }); 코드에 대한 각 구성 요소의 설명은 다음과 같습니다.

  • { userId }: 이 객체는 토큰에 포함될 payload입니다. 여기서는 userId라는 속성을 가진 객체를 사용하고 있습니다. 토큰이 해독되면 이 payload에 접근할 수 있습니다. 보통 payload에는 사용자 식별 정보나 권한 등을 포함합니다.

  • SECRET_KEY: 이 값은 JWT의 서명에 사용되는 비밀 키입니다. 이 비밀 키는 서버에서만 알려져 있어야 하며, 클라이언트나 외부에 노출되지 않아야 합니다. 비밀 키를 사용하여 토큰을 서명함으로써, 토큰이 손상되거나 변경되지 않았음을 검증할 수 있습니다.

  • { expiresIn: TOKEN_EXPIRATION }: 이 객체는 토큰에 대한 추가 옵션을 설정하는 데 사용됩니다. 여기서는 expiresIn이라는 속성을 사용하여 토큰의 만료 시간을 설정하고 있습니다. TOKEN_EXPIRATION 변수의 값은 "24h"로, 토큰이 생성된 후 24시간이 지나면 만료되도록 설정되어 있습니다. 만료된 토큰은 더 이상 유효하지 않으며, 클라이언트는 새로운 토큰을 발급받아야 합니다.

이 코드의 결과로 생성된 token 변수는 JWT 문자열을 담고 있습니다. 이 토큰은 클라이언트에게 전송되어 인증에 사용되며, 서버에서는 이 토큰을 검증하여 클라이언트의 요청이 유효한지 확인합니다.

배포:

함수를 배포하려면 firebase deploy --only functions 명령을 실행하세요.

배포 후, 생성된 URL을 사용하여 API에 액세스할 수 있습니다.
이렇게 하면 Firebase Functions를 이용하여 회원가입 및 로그인 기능을 구현한 API가 생성되었습니다.

환경변수를 사용해서 SECRET_KEY 설정하기

환경 변수를 사용하여 SECRET_KEY 값을 설정하는 방법은 다음과 같습니다.

  1. Firebase Functions의 환경 설정에 SECRET_KEY 값을 저장합니다.

    • Firebase CLI를 사용하여 SECRET_KEY 값을 저장하려면 다음 명령어를 실행합니다:
firebase functions:config:set jwt.secret_key="your-secret-key"

이렇게 하면 SECRET_KEY 값이 Firebase Functions의 환경 변수로 저장됩니다.
2. functions/index.js 파일에서 환경 변수 값을 불러와 SECRET_KEY 변수에 할당합니다.

  • 환경 변수 값을 불러오려면 다음 코드를 사용하세요:
const SECRET_KEY = functions.config().jwt.secret_key;
  • 이 코드는 기존 const SECRET_KEY = "your-secret-key"; 코드를 대체합니다.

이렇게 하면 SECRET_KEY 값이 환경 변수에서 로드되어 Firebase Functions에서 사용됩니다. 이 방법은 민감한 정보를 소스 코드에 직접 포함시키지 않고 외부에서 관리할 수 있어 보안상 좋습니다.

참고로, 로컬에서 개발 중일 때는 Firebase Functions의 환경 변수가 사용되지 않습니다. 로컬 개발을 위한 환경 변수 설정 방법은 다음과 같습니다.

  1. Firebase Functions의 루트 디렉토리에 .env 파일을 생성합니다.
  2. .env 파일에 다음 내용을 추가합니다:
SECRET_KEY=your-secret-key
  1. functions/index.js 파일에서 dotenv 패키지를 사용하여 로컬 환경 변수를 불러옵니다.
    • 먼저 dotenv 패키지를 설치하세요:
npm install --save dotenv
  • 그리고 functions/index.js 파일 맨 위에 다음 코드를 추가하세요:
require("dotenv").config();
  • 이제 const SECRET_KEY = process.env.SECRET_KEY; 코드를 사용하여 로컬 환경 변수에서 SECRET_KEY 값을 불러올 수 있습니다.

로컬 개발 환경에서도 환경 변수를 사용하여 SECRET_KEY 값을 관리할 수 있습니다. 이렇게 하면 민감한 정보를 소스 코드에 직접 포함시키지 않고 외부에서 관리할 수 있습니다.

클라이언트 애플리케이션에서 기능 통합

이제 클라이언트 측 애플리케이션에서 API에 요청을 보내 회원가입 및 로그인 기능을 사용할 수 있습니다. 다음 단계로 클라이언트 애플리케이션에 이 기능을 통합하는 방법을 살펴보겠습니다.

예를 들어, 클라이언트 애플리케이션에 회원가입 및 로그인 기능을 추가하려면 다음과 같은 절차를 따르십시오.

  1. 클라이언트 애플리케이션에서 회원가입 및 로그인 양식을 만듭니다. 이 양식은 사용자로부터 이메일 주소와 비밀번호를 입력받아야 합니다.

  2. 양식의 제출 버튼이 클릭되면, 클라이언트 애플리케이션은 해당 정보를 서버로 전송합니다. 이 때, 각각의 기능에 맞는 API 엔드포인트를 사용합니다.

    • 회원가입: POST /signup
    • 로그인: POST /login
  3. 서버로부터 응답을 받은 후, 클라이언트 애플리케이션은 JWT 액세스 토큰을 저장하고 인증에 사용합니다. 일반적으로 액세스 토큰은 로컬 저장소나 쿠키에 저장됩니다.

  4. 이후 인증이 필요한 요청을 보낼 때, 클라이언트 애플리케이션은 저장된 JWT 액세스 토큰을 사용하여 요청에 인증 정보를 포함시킵니다. 일반적으로 이 작업은 HTTP 헤더에 토큰을 포함시키는 방식으로 수행됩니다.

  5. 서버에서 요청을 받을 때, JWT 액세스 토큰의 유효성을 검사하고 사용자 인증이 완료된 후에만 요청을 처리합니다.

이렇게 클라이언트 애플리케이션과 서버 사이에서 회원가입 및 로그인 기능을 처리할 수 있습니다. 애플리케이션의 보안을 강화하고 사용자 경험을 개선하기 위해 추가적인 작업, 예를 들어 비밀번호 찾기 기능, 이메일 인증, 소셜 미디어 계정을 통한 로그인 등을 구현할 수도 있습니다.

클라이언트로부터 전송된 요청의 토큰 유효성을 검사하고 작업을 수행하기

Firebase Functions에서 클라이언트로부터 전송된 요청의 토큰 유효성을 검사하고 작업을 수행하는 코드 예시입니다. 이 예시에서는 jsonwebtoken 패키지를 사용하여 토큰을 검증합니다.

  1. 먼저, jsonwebtoken 패키지를 설치해야 합니다. Firebase Functions 프로젝트에서 다음 명령어를 실행합니다.
npm install jsonwebtoken
  1. functions/index.js 파일에 다음 코드를 추가합니다.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const jwt = require("jsonwebtoken"); // jsonwebtoken 패키지를 불러옵니다.
const cors = require("cors");

admin.initializeApp();
const SECRET_KEY = functions.config().jwt.secret_key;

const app = express();
app.use(cors({ origin: true }));

// JWT 토큰 검증 미들웨어 함수
const authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    res.status(401).send("Unauthorized: No token provided");
    return;
  }

  const token = authHeader.split(" ")[1];

  jwt.verify(token, SECRET_KEY, (err, decoded) => {
    if (err) {
      res.status(401).send("Unauthorized: Invalid token");
      return;
    }
    req.userId = decoded.userId;
    next();
  });
};

// 인증이 필요한 API 엔드포인트 예시입니다.
app.get("/protected", authenticate, (req, res) => {
  // 여기서 req.userId는 토큰에서 해독된 사용자 ID입니다.
  // 작업을 수행합니다.
  res.send(`Hello, user ${req.userId}! This is a protected endpoint.`);
});

exports.api = functions.https.onRequest(app);

위 코드에서 authenticate 함수는 미들웨어로 사용되며, 클라이언트로부터 전송된 요청의 토큰 유효성을 검사합니다. 토큰이 유효한 경우에만 next() 함수를 호출하여 다음 미들웨어나 요청 처리기로 이동합니다. 토큰이 유효하지 않거나 존재하지 않는 경우, 401 상태 코드와 함께 에러 메시지를 반환합니다.

app.get("/protected", authenticate, (req, res) => { ... })에서 authenticate 미들웨어가 요청 처리기 앞에 위치하므로, /protected 엔드포인트에 대한 요청은 먼저 토큰 유효성을 검사하게 됩니다. 토큰이 유효한 경우에만 요청 처리기가 실행되어 작업을 수행합니다.

0개의 댓글