/auth/github 엔드포인트를 호출합니다.
GitHub 로그인 페이지로 리다이렉트합니다.
GitHub에서 로그인하고 앱 권한 승인을 완료합니다.
Authorization Code를 포함하여 /auth/github/callback?code=abcd1234로 응답합니다.
Authorization Code를 사용해 GitHub에 요청을 보냅니다.Access Token을 반환합니다.
인증 상태를 유지하는 데 사용됩니다.소스 코드
import express from 'express';
import {
redirectToGitHub,
handleGitHubCallback,
} from '../controllers/authController';
const router = express.Router();
router.get('/github', redirectToGitHub);
router.get('/github/callback', handleGitHubCallback);
export default router;
import express from 'express';
import { redirectToGitHub, handleGitHubCallback } from '../controllers/authController';
expressredirectToGitHub, handleGitHubCallbackrouter.get('/github', redirectToGitHub);
router.get('/github/callback', handleGitHubCallback);
/github/github/callbackimport { Request, Response } from 'express';
import axios from 'axios';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
import { BadRequestError } from '../errors/httpError';
dotenv.config();
const {
GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET_KEY,
GITHUB_CALLBACK_URL,
JWT_SECRET_KEY,
} = process.env;
export const redirectToGitHub = (req: Request, res: Response): void => {
const redirectURI = `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${GITHUB_CALLBACK_URL}&scope=user:email`;
res.redirect(redirectURI);
};
export const handleGitHubCallback = async (
req: Request,
res: Response
): Promise<void> => {
const code = req.query.code as string;
if (!code) {
throw new BadRequestError();
}
try {
const tokenResponse = await axios.post(
'https://github.com/login/oauth/access_token',
{
client_id: GITHUB_CLIENT_ID,
client_secret: GITHUB_CLIENT_SECRET_KEY,
code,
},
{ headers: { Accept: 'application/json' } }
);
const accessToken = tokenResponse.data.access_token;
if (!accessToken) {
throw new Error('액세스 토큰을 얻지 못했습니다');
}
const userResponse = await axios.get('https://api.github.com/user', {
headers: { Authorization: `Bearer ${accessToken}` },
});
const userEmailResponse = await axios.get(
'https://api.github.com/user/emails',
{
headers: { Authorization: `Bearer ${accessToken}` },
}
);
const userInfo = {
id: userResponse.data.id,
username: userResponse.data.login,
email: userEmailResponse.data.find((email: any) => email.primary)?.email,
};
if (!userInfo.email) {
throw new Error('이메일을 찾을 수 없습니다');
}
const token = jwt.sign(userInfo, JWT_SECRET_KEY!, { expiresIn: '1h' });
res.status(200).json({
message: 'GitHub 로그인 성공',
token,
user: userInfo,
});
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Unknown error occurred';
res.status(500).json({
error: 'GitHub OAuth Failed',
details: errorMessage,
});
}
};
dotenv.config();
const {
GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET,
GITHUB_CALLBACK_URL,
JWT_SECRET_KEY,
} = process.env;
GITHUB_CLIENT_ID : GitHub OAuth 클라이언트 IDGITHUB_CLIENT_SECRET_KEY : 시크릿 키.GITHUB_CALLBACK_URL : GitHub 인증 후 응답을 받을 콜백 URL.JWT_SECRET_KEY : JWT 토큰 서명에 사용할 암호 키.
export const redirectToGitHub = (req: Request, res: Response): void => {
const redirectURI = `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}
&redirect_uri=${GITHUB_CALLBACK_URL}&scope=user:email`;
res.redirect(redirectURI);
};
https://github.com/login/oauth/authorize : GitHub OAuth 엔드포인트.client_id : GitHub OAuth 앱의 클라이언트 ID.redirect_uri : 인증 후 응답을 받을 서버 측 콜백 URL.scope=user:email : 사용자의 이메일 접근 권한을 요청.res.redirect(redirectURI) : 클라이언트를 GitHub OAuth 인증 페이지로 리다이렉트.http://localhost:5000/auth/github/callback?code=d20917d34b6684f443a4const code = req.query.code as string;
if (!code) {
res.status(400).json({ error: 'Authorization code is missing' });
return;
}
req.query.code검증const tokenResponse = await axios.post(
'https://github.com/login/oauth/access_token',
{
client_id: GITHUB_CLIENT_ID,
client_secret: GITHUB_CLIENT_SECRET,
code,
},
{ headers: { Accept: 'application/json' } }
);
https://github.com/login/oauth/access_tokenclient_id
client_secret_key
code
headers: { Accept: 'application/json' }
HTTP 요청 헤더를 설정하는 부분
GitHub OAuth API에 요청할 때 응답 데이터 형식을 지정하는 역할
헤더를 설정하지 않은 경우:
POST https://github.com/login/oauth/access_token
access_token=abcdef12345&scope=user&token_type=bearer
헤더를 설정한 경우
POST https://github.com/login/oauth/access_token
Accept: application/json
{
"access_token": "abcdef12345",
"scope": "user",
"token_type": "bearer ..."
}
```
const userResponse = await axios.get('https://api.github.com/user', {
headers: { Authorization: `Bearer ${accessToken}` },
});
https://api.github.com/userAuthorization 헤더const userEmailResponse = await axios.get(
'https://api.github.com/user/emails',
{
headers: { Authorization: `Bearer ${accessToken}` },
}
);
https://api.github.com/user/emailsprimary 필드const userInfo = {
id: userResponse.data.id,
username: userResponse.data.login,
email: userEmailResponse.data.find((email: any) => email.primary)?.email,
};
if (!userInfo.email) {
throw new Error('Primary email not found');
}
id : GitHub 사용자 고유 ID.username : 사용자명.email : 기본 이메일.const token = jwt.sign(userInfo, JWT_SECRET_KEY!, { expiresIn: '1h' });
payload : JWT에 저장할 사용자 정보 (userInfo).secret : JWT 서명 비밀 키.optionsres.status(200).json({
message: 'GitHub 로그인 성공',
token,
user: userInfo,
});
message: 성공 메시지.token: 발급된 JWT.user: 사용자 정보 (id, username, email).} catch (error) {
console.error('GitHub OAuth Error:', error);
res.status(500).json({ error: 'GitHub OAuth Failed' });
}