본문 바로가기
개발&프로그래밍

[HTTP] HTTP 인증방식 : Basic, Bearer, OAuth까지

by 재아군 2025. 2. 21.

HTTP 인증 방식 완벽 가이드: Basic, Bearer, OAuth까지

 

1. Basic 인증

가장 기본적인 인증 방식으로, 사용자 이름과 비밀번호를 Base64로 인코딩하여 전송합니다.

 

구현 예제

// 클라이언트 측
const username = 'user';
const password = 'pass';
const basicAuth = 'Basic ' + btoa(username + ':' + password);

fetch('https://api.example.com', {
    headers: {
        'Authorization': basicAuth
    }
});

// 서버 측 (Express)
const auth = require('basic-auth');

app.use((req, res, next) => {
    const credentials = auth(req);

    if (!credentials || credentials.name !== 'user' || credentials.pass !== 'pass') {
        res.setHeader('WWW-Authenticate', 'Basic');
        return res.status(401).send('인증 필요');
    }
    next();
});

 

장단점

  • 장점: 구현이 간단
  • 단점: 보안성이 낮음 (Base64는 쉽게 디코딩 가능)

 

 

2. Bearer 토큰 인증

토큰 기반 인증의 일종으로, JWT를 많이 사용합니다.

 

JWT 구조

헤더.페이로드.서명

 

JWT 구현 예제

const jwt = require('jsonwebtoken');

// 토큰 생성
app.post('/login', (req, res) => {
    const { username, password } = req.body;

    // 사용자 검증 후
    const token = jwt.sign(
        { id: user.id, username: user.username },
        process.env.JWT_SECRET,
        { expiresIn: '1h' }
    );

    res.json({ token });
});

// 토큰 검증 미들웨어
const verifyToken = (req, res, next) => {
    const bearerHeader = req.headers['authorization'];

    if (!bearerHeader) {
        return res.status(401).json({ message: '토큰이 필요합니다' });
    }

    const token = bearerHeader.split(' ')[1];

    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded;
        next();
    } catch (err) {
        res.status(401).json({ message: '유효하지 않은 토큰입니다' });
    }
};

 

3. OAuth 2.0

외부 서비스의 인증을 사용하는 방식입니다.

 

구현 예제 (Google OAuth)

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: "http://localhost:3000/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
    User.findOrCreate({ googleId: profile.id }, function (err, user) {
        return cb(err, user);
    });
}));

// 라우트 설정
app.get('/auth/google',
    passport.authenticate('google', { scope: ['profile', 'email'] })
);

app.get('/auth/google/callback',
    passport.authenticate('google', { failureRedirect: '/login' }),
    function(req, res) {
        res.redirect('/');
    }
);

 

4. 리프레시 토큰

액세스 토큰의 보안을 강화하기 위한 방식입니다.

 

구현 예제

// 토큰 발급
app.post('/login', async (req, res) => {
    const { username, password } = req.body;

    // 사용자 검증 후
    const accessToken = jwt.sign(
        { id: user.id },
        process.env.JWT_SECRET,
        { expiresIn: '15m' }
    );

    const refreshToken = jwt.sign(
        { id: user.id },
        process.env.REFRESH_SECRET,
        { expiresIn: '7d' }
    );

    // 리프레시 토큰 저장
    await User.updateOne(
        { _id: user.id },
        { refreshToken }
    );

    res.json({ accessToken, refreshToken });
});

// 토큰 갱신
app.post('/refresh', async (req, res) => {
    const { refreshToken } = req.body;

    try {
        const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
        const user = await User.findOne({ 
            _id: decoded.id,
            refreshToken 
        });

        if (!user) {
            throw new Error('Invalid refresh token');
        }

        const accessToken = jwt.sign(
            { id: user.id },
            process.env.JWT_SECRET,
            { expiresIn: '15m' }
        );

        res.json({ accessToken });
    } catch (err) {
        res.status(401).json({ message: '토큰 갱신 실패' });
    }
});

 

5. API Key 인증

서비스 간 인증에 주로 사용됩니다.

 

구현 예제

const apiKeyMiddleware = async (req, res, next) => {
    const apiKey = req.headers['x-api-key'];

    if (!apiKey) {
        return res.status(401).json({ message: 'API key required' });
    }

    try {
        const client = await ApiClient.findOne({ apiKey });
        if (!client) {
            throw new Error('Invalid API key');
        }

        req.client = client;
        next();
    } catch (err) {
        res.status(401).json({ message: '유효하지 않은 API key' });
    }
};

 

6. 보안 고려사항

 

HTTPS 사용

const express = require('express');
const https = require('https');
const fs = require('fs');

const options = {
    key: fs.readFileSync('private.key'),
    cert: fs.readFileSync('certificate.crt')
};

https.createServer(options, app).listen(443);

 

XSS 방지

app.use(helmet()); // 보안 헤더 설정

// JWT 토큰을 HttpOnly 쿠키로 저장
res.cookie('token', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict'
});

 

CSRF 방지

const csrf = require('csurf');
app.use(csrf({ cookie: true }));

app.get('/form', (req, res) => {
    res.render('form', { csrfToken: req.csrfToken() });
});

 

 

댓글