🎯 Achievement Goals


  • OAuth 2.0에 대해 이해한다.
  • 토큰 / OAuth를 통해 인증 구현을 할 수 있다.





👉 OAuth 2.0


그동안 웹이나 앱에서 흔히 찾아볼 수 있는 소셜 로그인 인증 방식은 OAuth 2 라는 기술을 바탕으로 구현되었다. 내가 흔히 사용하는 구글 인증 로그인은 다 OAuth에서 비롯된 것이였다.

전통적으로 직접 작성한 서버에서 인증을 처리해주는 것과는 달리, OAuth는 인증을 중개해주는 매커니즘이다. 보안된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공하는 프로세스를 단순화하는 프로토콜이다.

쉽게 말하면 OAuth는 출입증을 발급해주는(?) 역할을 한다고 보면 이해하기 쉬운 것 같다.

이미 사용자 정보를 가지고 있는 웹 서비스(GitHub, Google, facebook 등)에서 대신 출입증을 발급해주고, 이 출입증을 가지고 웹에서 인증이 가능해지는 구조이다.

하지만 명심해야 할 것, OAuth는 인증(Authentication)을 다른 서비스에 맡길 뿐, 접근 권한 관리(Authorization)는 온전히 내 서버의 몫이다.


한마디로, 보안 된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공하는 프로세스를 단순화하는 프로토콜 중 한 방법이다.



OAuth에서 꼭 알아야 할 용어

  • Resource Owner : 액세스 중인 리소스의 유저


  • Client : Resource Onwer를 대신하여 보호된 리소스에 액세스하는 응용 프로그램


  • Authorization Server : Resource Sever가 액세스 토큰을 발급받는 서버이다. 즉, 클라이언트 및 리소스 소유자를 성공적으로 인증한 후 액세스 토큰을 발급하는 서버를 말한다.


  • Authorization Grant : 클라이언트가 액세스 토큰을 얻을 때 사용하는 자격 증명의 유형이다.


  • Authorization Code : access token을 발급받기 전에 필요한 code이다. client ID로 이 code를 받아온 후, client secet과 code를 이용해 Access token을 받아온다.


  • Access Token : 보호된 리소스에 액세스하는 데 사용되는 Credentials이다. 이 Access Token으로 이제 resource server에 접근할 수 있다.


  • Scope : scope는 토큰의 권한을 정의한다. 주어진 액세스 토큰을 사용하여 액세스할 수 있는 리소스의 범위이다.



소셜 로그인 로직 플로우


auth1


위 사진의 로직만큼은 눈감고도 그릴 수 있을 때까지 들여다보자!





👉 GitHub 인증 로그인 구현하기


오늘 과제는 내 앱에 Github을 등록하고 Github으로 인증 하고 로그인하는 기능을 구현하는 것이었다.



Github 등록


github

먼저 Github Settings에서 저 목록에 들어가면 OAuth Apps에서 내 앱을 등록할 수 있다. 등록을 하면 바로 client ID와 client Secret을 받을 수 있다!


githubkey


Authorization Callback URL은 OAuth 메커니즘 인증 과정이 끝난 후 리디렉션을 통해 다시 내 앱으로 이동하는 원리이다.






Server: Access token (OAuth에게)요청


서버에서 할 일은 클라이언트에서 받아온 Authrization Code를 서버의 /callback 엔드포인트로 전달해 서버에서 github App에게 Access Token 발급을 요청하는 것이다.

먼저 인증 정보를 바탕으로 Access Token을 받아올 수 있도록 도와주는 라우터를 구현했다.


// 생략...

await axios.post('https://github.com/login/oauth/access_token', {
    client_id: clientID,
    client_secret: clientSecret,
    code: req.body.authorizationCode,
  }, {
    hearders: {
      accept: 'application/json'
    }
  })
  .then(data => {
    const accessToken = data.data.access_token;
    console.log("data", data.data)
    res.status(200).send({ accessToken: accessToken })
  });)

// data {
//   access_token: 'fake_access_token',
//   token_type: 'Bearer',
//   scope: 'user'
// }


axios의 두번째 인자에AuthorizationCode를 담아서 보낸다. 이 요청을 통해서 Access token 을 받아온다.




Server: Access token (client에게)전달


이 라우터는 받아온 Access token을 확인한 후, local에 저장되어 있는 resource images를 클라이언트로 보내주는 라우터이다.

// 생략...

const authorization = req.headers.authorization;
 console.log(req.headers)

if (!authorization) {
  return res.status(403).send({ message:'no permission to access resources' });
}
else {
  return res.status(200).send({ images: images })
}

// headers {
//   host: '127.0.0.1:33333',
//   'accept-encoding': 'gzip, deflate',
//   authorization: 'token fake_access_token',
//   connection: 'close'
// }


headers에 Access token이 있다면 응답으로 images를 보내주었다.




Client: Authorizaiton Code 받아오기


Github App에게 요청을 보내 Authorization Code를 받아오는 일이다.

// 생략...

this.GITHUB_LOGIN_URL = 'https://github.com/login/oauth/authorize?client_id=client_id';

socialLoginHandler() {
    window.location.assign(this.GITHUB_LOGIN_URL)
  }

  render() {
    <button
      onClick={this.socialLoginHandler}
      className='socialloginBtn'
    >
      Github으로 로그인
    </button>
  }



Client: Access token 받아오기


위에서 받은 Authorization Code를 Server에 보내서 다시 Access token으로 돌려받아야 한다. Access token은 보안 유지가 필요하기 때문에 클라이언트에서 직접 OAuth App에 요청을 하는 방법은 보안에 매우 취약하다. 그렇기 때문에 Server로 보내주고 Access token 요청을 하는 것이 적절하다.


// 생략...
constructor() {
    super();
    this.state = {
      isLogin: false,
      accessToken: '',
    };
// 생략...

  await axios.post('http://localhost:8080/callback', {
    authorizationCode
  },  { withCredentials: true })
  .then(res => {
    const accessToken = res.data.accessToken;
    this.setState({
      isLogin: true,
      accessToken: accessToken,
    })
  })


Server에 authorizationCode을 전달하고 AccessToken을 받아온다.



Client: 리소스 서버에 리소스 요청


받아온 Access Token으로 리소스에 대한 API 요청을 할 수 있다. Access Token은 앞서 배웠던 Bearer Tokenm을 headers에 담아서 전달해줄 수 있다.

받아와야 할 리소스

  1. 먼저 Github API 요청에 Access token을 함께 보내어 유저 정보를 받아온다.
  2. local server에 저장된 이미지들을 받아온다.


// 생략...
constructor(props) {
    super(props);
    this.state = {
      images: [],
    }
}

// 1. Github API에 요청
await axios.get('https://api.github.com/user', {
      headers: {
        authorization: `token ${this.props.accessToken}`,
        accept: 'application/json'
      }
    }, { withCredentials: true })
    .then(res => {
      const { name, login, html_url, public_repos } = res.data;
      this.setState({
        name,
        login,
        html_url,
        public_repos
      })
    })

// 2. 저장된 이미지들을 받아온다.
await axios.get('http://localhost:8080/images', {
      headers: {
        authorization: `token ${this.props.accessToken}`
      }
    }, { withCredentials: true })
    .then(res => {
      this.setState({
        images: res.data.images
      })
    })


이렇게 Github을 이용해 OAuth 인증 로그인을 간단하게 구현을 해보았다!








🙌 느낀점


웹의 꽃이라고도 불릴 만한 인증 스프린트를 모두 다 마쳤다! 이로써 코드스테이츠 수업 99%를 수강했다!! 이제 다음주 월요일에 있을 마지막 H.A시험만 무사히 통과하고 하루동안 AWS 배포 스프린트를 끝내면 드디어 프로젝트 기간이 시작된다.

프로젝트 기간을 위해 14주동안 달려왔다고 생각해도 무방하다. 올해 1월부터 정말 쉴새 없이 달려왔다. 한 번에 엄청 많은 지식들을 습득해서 정리가 잘 되어있는건지는 모르겠지만.. 그래도 뭔가 많이 배운건 확실하다. 처음 코딩을 배웠던 작년 12월보다는 훨씬 더 성장한 모습이다.

끝날때까지 끝난게 아니니 주말에 복습 잘 하고 마지막 H.A시험을 보고 하루빨리 팀원들과 프로젝트를 하고 싶다!

프로젝트까지 끝나고 총 20주가 지난, 6월의 시점에서 내가 얼마나 더 성장해있을지 정말 궁금하다!





👊 내일의 TIW(today I Will)

주말동안 스프린트 복습!!