관리 메뉴

Mini

24.10.14. 개발일지 // github oauth구현 본문

JS/boostCamp

24.10.14. 개발일지 // github oauth구현

Mini_96 2024. 10. 14. 23:04

https://docs.github.com/ko/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps

 

OAuth 앱 권한 부여 - GitHub Docs

다른 사용자가 OAuth app에 권한을 부여하도록 설정할 수 있습니다.

docs.github.com

 

 

* github oauth

  • 깃헙 > 세팅 > dev > oauth app 생성

  • cid ,c pw 발급받기
  • auth/github로 접근시 깃허브로 redirect -> get요청 보내기
dotenv.config(); // env환경변수 파일 가져오기

export class GitAuthController implements ControllerV6{
    
    async process(req: Request, res: Response, paramMap: Map<string, string>, model: Map<string, object>) {
        const clientId: string = process.env.GITHUB_CLIENT_ID;

        const githubAuthURL = `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=user:email`;
        res.redirectGithub(githubAuthURL);
        
        req.isEnd=true;
        return "";
    }

    version6() {
    }
}
public redirectGithub(path: string) {
    this.status(302).header("Location", path).send();
}

auth/github요청시 결과

  • 승인을누르면 깃허브가 사용자에게 callbackurl로 code라는 쿼리파라미터를 가지고 다시요청하라고 명령한다.
  • 내 서버는 이 code를 가지고 엑세스 토큰을 요청한다.

승인을 누른후 내서버로 다시 요청이 왔다. with code
이 code를 가지고 post를 보냄 => access token을 얻어온다.
깃허브에서 이런 객체를 던져준다.
엑세스 토큰을 이용해서 깃허브에 get요청을보내서 user정보를 받아온다.
email은 추가요청필요!

  • accesstoken으로 github에서 받은 정보들로 회원가입 && 세션생성
  • username은 깃허브username + _github 컨벤션 적용.
  • 이때, 비밀번호는 uuid이용 => 수동 로그인 방지
/**
 * email 배열중
 * primary가 true인 email을 찾아서 사용
 */
const primaryEmail = emailsResponse.data.find((email: any) => email.primary).email;


/**
 * 이메일로 회원가입 여부 확인 && 없으면 새로회원가입, 세션생성
 * 비밀번호설정 : uuid로 설정 => 수동 로그인 막기
 */
let findMember = await this.memberRepositoryCustom.findByEmail(primaryEmail);
if (!findMember) {
    const newMember = new Member(primaryEmail, userResponse.data.login +"_github", uuidv4());
    await this.memberRepository.save(newMember);
    findMember = newMember;
}
this.sessionMgr.createSession(findMember, res);

return "redirect:index";

결과

  • 전체코드
import {Request} from "../../was/request";
import {Response} from "../../was/response";
import {ControllerV6} from "../../frontcontroller/v6/ControllerV6";
import * as dotenv from 'dotenv';
import {MemberRepository} from "../member/MemberRepository";
import {SessionManager} from "../../utils/SessionManager";
import axios from 'axios';
import {Member} from "../member/Member";
import { v4 as uuidv4 } from 'uuid';

dotenv.config(); // env환경변수 파일 가져오기

export class GitAuthCallbackController implements ControllerV6{

    private memberRepositoryCustom = MemberRepository.getInstance();
    private memberRepository = MemberRepository.getInstance().getRepo();
    private sessionMgr : SessionManager = SessionManager.getInstance();

    async process(req: Request, res: Response, paramMap: Map<string, string>, model: Map<string, object>) {
        const clientId: string = process.env.GITHUB_CLIENT_ID;
        const clientSecret: string = process.env.GITHUB_CLIENT_SECRET;
        const redirectURI = 'http://localhost:3000/auth/github/callback';
        const { code } = req.query;

        const tokenResponse = await axios.post('https://github.com/login/oauth/access_token', {
            client_id: clientId,
            client_secret: clientSecret,
            code,
            redirect_uri: redirectURI,
        }, {
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
        });

        const accessToken = tokenResponse.data.access_token;
        const userResponse = await axios.get('https://api.github.com/user', {
            headers: {
                Authorization: `token ${accessToken}`,
            },
        });
        // console.log(userResponse);

        /**
         * email은 추가 요청해야함.
         * 기본 응답에 포함되지않음.
         * 이래서 처음 권한받을때 scope에 email을 받은것임.
         */
        const emailsResponse = await axios.get('https://api.github.com/user/emails', {
            headers: {
                Authorization: `token ${accessToken}`,
            },
        });

        /**
         * email 배열중
         * primary가 true인 email을 찾아서 사용
         */
        const primaryEmail = emailsResponse.data.find((email: any) => email.primary).email;


        /**
         * 이메일로 회원가입 여부 확인 && 없으면 새로회원가입, 세션생성
         * 비밀번호설정 : uuid로 설정 => 수동 로그인 막기
         */
        let findMember = await this.memberRepositoryCustom.findByEmail(primaryEmail);
        if (!findMember) {
            const newMember = new Member(primaryEmail, userResponse.data.login +"_github", uuidv4());
            await this.memberRepository.save(newMember);
            findMember = newMember;
        }
        this.sessionMgr.createSession(findMember, res);

        return "redirect:index";
    }

    version6() {
    }
}

 

 

* 프론트 구현

  • 간단하다. 로그인페이지에 링크하나 추가하면된다.
<div class="login-button-block-github">
    <button type="button" class="github-button" onclick="location.href='/auth/github'">
        <svg class="github-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="white">
            <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
        </svg>
        깃허브로 로그인
    </button>
</div>
/* git login css*/
.login-button-block-github {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 15px;
    font-family: Arial, sans-serif;
}
.github-button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background-color: #24292e;
    color: white;
    border: none;
    border-radius: 6px;
    padding: 10px 20px;
    font-size: 16px;
    font-weight: bold;
    cursor: pointer;
    transition: background-color 0.3s ease;
}
.github-button:hover {
    background-color: #2c3238;
}
.github-icon {
    width: 24px;
    height: 24px;
}
.login-text {
    font-size: 14px;
    color: #586069;
}
.login-link {
    color: #0366d6;
    text-decoration: none;
    font-weight: bold;
}
.login-link:hover {
    text-decoration: underline;
}

결과

 

 

* csrf 공격

  • 배경 

  • 개념

 

 

 

* fetch vs axios

  • 간단한 axois를 사용하고자 함.