[S3U7]OAuth

Yeong·2023년 3월 9일
0

CS

목록 보기
6/12

📖OAuth

OAuth는 인증을 중개해주는 매커니즘이다.
이미 사용자 정보를 가지고 있는 웹 서비스(Naver, Kakao, Google 등)에서 사용자의 인증을 대신해주고, 접근 권한에 대한 토큰을 발급한 후, 이를 이용해 내 서버에서 인증을 한다.

🏷️OAuth의 주체

  • Resource Owner
    OAuth 인증을 통해 로그인하고 싶은 사용자
  • Resource Server & Authorization Server
    사용자가 이미 사용중인 서비스(Naver, Kakao, Google 등)
    • Resource Server: 사용자의 정보를 저장하고 있는 서버 특정
    • Authorization Server: 인증을 담당하는 서버 특정
  • Application
    사용자가 새로 로그인하고 싶은 서비스

🏷️OAuth 인증 방식의 종류

Implicit Grant Type


기존 서비스에 로그인만 되어있다면 새로운 서비스에 바로 액세스 토큰을 내어주기 때문에 보안성이 조금 떨어진다.

Authorization Code Grant Type


Authorization Code를 사용한 인증 단계가 추가로 있기 때문에 비교적 더 안전하다.
그러나 사용자가 새로운 서비스를 이용하다가 액세스 토큰이 만료되었을 때, 매번 이 과정을 거쳐서 액세스 토큰을 다시 발급받아야 한다면 사용자 편의성에 있어서는 좋지 않을 수 있다.


1. 사이트 접속
2. 로그인
client에서 Authorizaiton Server로 인증 요청을 보낸다

  const loginRequestHandler = () => {
    // GitHub URL로 사용자 인증 요청
    // OAuth 인증이 완료되면 authorization code와 함께 callback url로 리디렉션
    return window.location.assign(
      `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}`
    );
  };

3~4. Authorization Code 전달
인증 요청을 받은 Authorizaiton Server는 유효한 인증 요청인지 확인한 후 Authorization Code를 발급해 다시 클라이언트로 (url 파라미터를 통해) 보내준다.
그리고 클라이언트는 Authorization Code를 로컬 서버에 보내준다.

// Authorization Server로부터 클라이언트로 리디렉션된 경우, Authorization Code가 함께 전달된다.  
//ex) http://localhost:3000/mypage?code=5e52fb85d6a1ed46a51f
  useEffect(() => {
    const url = new URL(window.location.href);
    const authorizationCode = url.searchParams.get('code');
  }, []);

// 받아온 Authorization Code를 로컬 서버로 보낸다. 
// 왜냐하면  Access Token은 보안 유지가 필요하기 때문에 
//클라이언트에서 직접 OAuth App에 요청을 하는 방법은 보안에 취약할 수 있다.
// 그러므로 보안을 위해 Authorization Code를 서버로 보내주고 서버에서 Access Token 요청을 하는 것이 적절하다.
    const getAccessToken = async (authorizationCode) => {
      return axios
        .post('http://localhost:4000/callback', {authorizationCode}) // 로컬 서버로 보냄
        .then((res) => { //요청 성공하면 로컬 서버로부터 Access Token 받고 로그인됨
          setAccessToken(res.data.accessToken)
          setIsLogin(true)
        })

5~6. Local Server에서 Authorization Server으로 액세트 토큰 요청 및 액세트 토큰 전달
Authorization Code를 받은 로컬 서버는 Authorization Code과 함께 액세스 토큰을 Authorizaiton Server에 요청한다.
Authorizaiton Server는 유효한 Authorization Code인지 확인한 후 액세스 토큰을 발급해
로컬서버로 액세스 토큰을 전달한다.

module.exports = async (req, res) => {
  // req의 body로 authorization code가 들어온다.
  try {
    const result = await axios({  // 액세트 토큰 요청
      method: 'post',
      url: `https://github.com/login/oauth/access_token`,
      headers: {
        accept: 'application/json',
      },
      data: {
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        code: req.body.authorizationCode,
      },
    });
    const accessToken = result.data.access_token;//인증성공 시 accessToken 받음
    return res.status(200).send({ accessToken });// 전달 받은 accessToken 클라이언트로 전달
  } catch (err) {
    return res.status(401).send({ message: 'error' });
  }
};

7~9. 액세스 토큰을 담아 Resource Server로 사용자의 정보를 요청
Resource Server는 Application에게서 전달 받은 액세스 토큰이 유효한 토큰인지 확인 후 사용자의 정보를 로컬서버에 보내준다.

  useEffect(() => { // 클라이언트가 로컬서버로 accessToken과 함께 사용자 정보 요청
    axios.post('http://localhost:4000/userinfo', {accessToken}) 
      .then((res) => { // 요청 성공시 정보 받아서 상태 변경
        setGithubUser(res.data.githubUserData)
        setServerResource(res.data.serverResource)
        setIsLoading(false)
      })  
  }, []);
module.exports = async (req, res) => {
  const { accessToken } = req.body;
  // 클라이언트에서 전달받은 access token를 이용해 Resource Server로 사용자의 정보요청
  return axios
    .get('https://api.github.com/user', {
      headers: {
        Authorization: `token ${accessToken}`,
      },
    })
    .then((res) => res.data) // 성공시 사용자 정보 받음
    .then((githubUserData) => { // 받은 정보 클라이언트로 전달
      res.send({ githubUserData, serverResource });
    })
    .catch((e) => {
      res.sendStatus(403);
    });
};
  1. 로그아웃
    클라이언트에서 액세스 토큰을 이용해 로컬서버로 로그아웃 요청한다.
    엑세스 토큰을 전달받은 로컬서버는 Resource Server에게 엑세스 토큰과 함께 로그아웃 요청한다.
 const logoutHandler = () => {
    // prop으로 받은 Access Token을 이용해 /logout 엔드포인트로 로컬서버에게 로그아웃 요청
    axios.delete('http://localhost:4000/logout', {data: {accessToken}})
      .then((res) => { // 요청 성공 로그아웃으로 상태 변경
        setIsLogin(false);
        setGithubUser(null);
        setServerResource(null);
        setAccessToken(null)
      })
  };

axios.delete 로 body에 정보 실어 보내는 법
post와 다르게 그냥 {accessToken}에 담아 보내면 안되고 {data: {accessToken}} 이런 형식으로 보내주어야 서버에서 req.body로 받을 수 있다.

module.exports = (req, res) => {
  const { accessToken } = req.body;
  // 클라이언트에서 전달받은 access token를 이용해 사용자의 권한 부여를 취소 요청
  axios
    .delete(`https://api.github.com/applications/${CLIENT_ID}/token`, {
      data: {
        access_token: accessToken,
      },
      auth: {
        username: CLIENT_ID,
        password: CLIENT_SECRET,
      },
    })
    .then(() => {
      res.status(205).send('Successfuly Logged Out');
    })
    .catch((e) => {
    });
};

Refresh Token Grant Type

🏷️OAuth의 장점

  • 가입이 간편하다
  • 정보를 해당 서비스에 직접 노출하지 않기 때문에 안전하다.
  • 회원정보를 직접 가짐으로 발생할 수 있는 유출 위험 부담이 줄어든다.
  • 사용자가 권한 영역을 설정할 수 있다.
profile
긍정적으로~✍️(◔◡◔)

0개의 댓글