PHP를 이용해서 "Google+ 계정으로 로그인" 구현하는 방법


1. 세션을 시작한다.

  session_start();




2. 구글에 등록해놓은 정보들을 이용해서 로그인해야 하므로, 변수에 잘 저장해 놓자.

  $g["client_id"]     = '483---.apps.googleusercontent.com';
  $g["client_secret"] = 'JVa---c5vILY0';
  $g["redirect_uri"]  = 'http://www.mysite.co.kr/callback-google.php';




3. 구글로 로그인하는 버튼을 만들고, 거기에 링크를 걸어준다.

  if( !isset($_SESSION['state']) )
    $_SESSION['state'] = urlencode( md5( microtime().mt_rand() ) );
  $login_url = 
    "https://accounts.google.com/o/oauth2/auth?".
    "response_type=token".
    "&passive=true".
    "&client_id=" . $g["client_id"] . 
    "&state=" . $_SESSION['state'] . 
    "&redirect_uri=" . urlencode( $g["redirect_uri"] ) .
    "&scope=" . urlencode( "openid email" );
  echo "<br><a href='". $login_url ."'> Google+ 계정으로 로그인 </a><br>";




4. 사용자가 로그인 버튼을 클릭하면 Google 사이트로 이동해서 로그인하는 과정을 거친다.
로그인이 끝나면 callback uri에 적어놓은 주소로 사용자가 이동해 들어오게 되는데, 다음과 같은 주소를 가지고 들어온다.

  http://www.mysite.co.kr/callback-google.php#
    state=ouFvDAK6---&access_token=ya29---&
    token_type=Bearer&
    expires_in=3600&
    authuser=0&
    num_sessions=1&
    hd=mysite.com&
    session_state=826dd---&
    prompt=none

그런데 이 주소에 #이 사용되었기때문에 서버에서는 #을 포함해 그 뒷부분에 무엇이 있는지 알 수가 없다. 그래서, #이후의 정보에 접근할 수 있는 브라우저에서 javascript를 실행시켜야 한다.

  <script>
    location.href = 'http://www.mysite.co.kr/callback-google.php?hash=1&' +
                      location.hash.substring(1);
  </script>  




5. 사용자는 Javascript의 location.href 변경을 통해서 웹서버로 다시 찾아오게 된다. Javascript 덕분에 이제 모든 파라미터들을 php에서도 읽을 수 있게 되었다. 파라미터 중에 access_token이 있는지 여부에 따라 처리가 달라져야 한다.

  if( isset( $_GET["access_token"] ) ) {
    // 사용자가 정보공유를 허가한 경우이다
    $_SESSION['access_token'] = $_GET['access_token'];
    $_SESSION['token_type']   = $_GET['token_type'];
    $_SESSION['expires_at']   = $_GET['expires_in'] + microtime(true);

    validate_token_info();
  }
  else {
    // 사용자가 정보공유를 불허한 경우이다
  }


validate_token_info()에서는 사용자의 email 주소를 알아낸다.

  function validate_token_info() {
    $url = "https://www.googleapis.com/oauth2/v1/tokeninfo?" .
         "access_token=" . $_SESSION["access_token"];
    $c = curl_init();
    curl_setopt($c, CURLOPT_URL, $url);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, true); 
    $r = curl_exec($c); 
    curl_close($c);
    $j = json_decode($r, true);
    $_SESSION['userid'] = $j["email"] ? 
                $j["email"] : "nothing";
  }


(token info의 예)
{
  "iss":"accounts.google.com",
  "at_hash":"HK6E_P6Dh8Y93mRNtsDB1Q",
  "email_verified":"true",
  "sub":"10769150350006150715113082367", ==> 사용자의 이메일주소는 변할지언정 sub값은 평생토록 변하지 않는다
  "azp":"1234987819200.apps.googleusercontent.com",
  "email":"jsmith@example.com", ==> 유일하지 않은 값이다. 중복될 수 있다.
  "aud":"1234987819200.apps.googleusercontent.com",
  "iat":1353601026,
  "exp":1353604926  ==> id token이 폐기되는 시각
}




6. Access Token 폐기시키는 방법
Access Token을 폐기시킨다고 해서 서비스에서 로그아웃 되는것은 아니다.

  if( isset( $_SESSION['access_token'] ) ) {
    $url = "https://accounts.google.com/o/oauth2/revoke?".
         "token=".$_SESSION['access_token'];
    echo "<br><a href='". $url ."'> Access Token 폐기 </a><br>";
  }




구글 계정으로 로그인하는 방법을 사용해보니, 맺고 끊음이 불분명한 것이 치명적인 약점으로 작용함을 알겠더라. 사용자에게 비밀번호를 한 번 더 입력하라는 요구를 하고싶어도 그것이 불가능한 것이 바로 이 "남의 서비스 계정으로 내 서비스에 로그인하기"의 세계다.

세상에 완벽하게 좋은 공짜는 없어!