네이버 로그인 API로 로그인 기능 만들기

Category
server-side, 개발 노트
Posted
2014-10-29 11:12
세월호 참사 1주기 결코 잊지 않겠습니다.

Prologue

필자는 현재 네이버 카페 하드코딩하는사람들(이후 하코사)에서 JavaScript 기초 스터디를 진행하고 있다.
이 짓(?)을 왜 하는지에 대해서는 차후 블로그에 포스팅 하게 될지는 모르겠으나… 그건 그 때가서 한 번 끄적여보는 기회를 가지도록 하고… 쿨럭…
스터디를 강의형태로 진행을 하다 보니 강의 자료가 reveal.js를 이용하여 웹 슬라이드로 만들어져있다.

그런데, 문제는 이 웹으로 만들어져있다보니 너무 쉽게 노출이 된다는 점…
일정한 금액을 받고 스터디를 진행하고 있기에 이 자료를 완전히 노출시키고 싶지는 않았다.
그래서 처음에 만들어 놓은게 ID / PW 로그인 방식으로 제공을 하는 것. 최초에 기본 비밀번호를 제공해놓고 처음 로그인 시 무조건 비밀번호를 변경하도록 하게끔 만들어 두었는데, 이게 생각보다 영~ 불편했다 ㅠ_ㅠ ;;
간간히 비밀번호를 잊는 분들도 계셔서 다시 초기화를 해야 하거나… (암호화 되어 있어서 필자도 못 보는…쿨럭…) 혹은 ID/PW를 누군가와 공유하는 것을 근본적으로 차단할 수 없다는 문제…
그렇다… 필자는 치사하게시리(?) 이 자료에 대한 공유를 그다지 좋아하지 않는다. 어디 내보일만한 자료도 아닐뿐더러 스터디 하시는 분들은 어쨋든 일정한 금액을 지불하고 보는 건데 그걸 아무런 제약도 없이 다 볼 수 있다라는건… 개인적으로 좀 그렇다… = _=a

어쨌든, 이런저런 문제들을 해결할 방법들을 고민하다가 어차피 ID를 네이버의 것으로 쓰고 있으니 만큼, 네이버 로그인으로 교체하기로 하여 바로 작업에 착수하여 2 ~ 3 시간 정도를 소요해서 결과를 만들어 냈다.

Episode

네이버 로그인을 만들기 위해 네이버 로그인 API 문서를 찾아봤었다. 젠장… 네이버 API 문서는 내게 너무 불친절했다. ㅠ_ㅠ
개발 가이드 문서를 보기 위해 설마 링크를 그렇게 타고 가야 할 줄은 상상도 못했다.
"오픈 API" → "네이버 로그인" → 네이버 로그인 API 소개 페이지 내에 있는 "네이버 아이디로 로그인 자세히 보기" → 자세히 보기 페이지 내 페이지 제~~~일 하단에 있는 "네이버 아이디로 로그인 개발하기 – PC/Mobile Web" …
개발 문서 하나 보자고 이렇게 문서를 자세히 다 읽어야 링크를 찾을 수 있고 타고 타고 타고 들어가야 한다니… 하아…

무튼 힘겹게(?) 찾은 개발 가이드 문서, Sample Code 그리고 하코사 미루님이 하코사에 올려둔 글을 참고하여 하나의 클래스에 담아서 만들었다.

Source Code

<?php
	define( 'NAVER_OAUTH_AUTHORIZE_URL', 'https://nid.naver.com/oauth2.0/authorize' );
	define( 'NAVER_OAUTH_TOKEN_URL', 'https://nid.naver.com/oauth2.0/token' );
	define( 'NAVER_GET_USERINFO_URL', 'https://apis.naver.com/nidlogin/nid/getUserProfile.xml');
	class OAuthRequest{
		private $client_id;
		private $client_secret;
		private $redirect_url;
		private $state;
		private $session;
		private $authorize_url = NAVER_OAUTH_AUTHORIZE_URL;
		private $accesstoken_url = NAVER_OAUTH_TOKEN_URL;
		private $code;
		private $tokenArr; 
		private $userInfo;

		function __construct( $client_id, $client_secret, $redirect_url) {
			$this -> client_id = $client_id;
			$this -> client_secret = $client_secret;
			$this -> redirect_url = $redirect_url;
			if(!isset($_SESSION)) {
				session_start();
			}
		}
		private function generate_state(){
			$mt = microtime();
			$rand = mt_rand();
			$this -> state = md5( $mt . $rand );
		}
		public function set_state(){
			$this -> generate_state();
			$_SESSION['state'] = $this -> state;
		}
		private function get_code(){
			$this -> code = $_GET['code'];
		}
		private function get_state(){
			$this -> state = $_SESSION['state'];
			return $this -> state;
		}
		public function request_auth(){
			header('Location: '. $this -> get_request_url() );
		}
		public function get_request_url(){
			return $this -> authorize_url . '?response_type=code&client_id=' . $this -> client_id . '&state=' . $this -> state . '&redirect_url=' . urlencode($this -> redirect_url); 
		}
		public function get_accesstoken_url(){
			return $this -> accesstoken_url . '?grant_type=authorization_code&client_id=' . $this -> client_id . '&client_secret=' . $this -> client_secret . '&code=' . $this -> code . '&state= ' . $this -> state;
		}
		public function call_accesstoken(){
			$this -> get_code();
			$this -> get_state();

			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $this -> get_accesstoken_url() );
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false );
			curl_setopt($ch, CURLOPT_COOKIE, '' );
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
			$g = curl_exec($ch);
			curl_close($ch);
			$data = json_decode($g, true);
			$this -> tokenArr = array(
				 'Authorization: '.$data['token_type'].' '.$data['access_token']
			);
		}
		public function get_user_profile(){
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, NAVER_GET_USERINFO_URL );
			curl_setopt($ch, CURLOPT_HTTPHEADER, $this -> tokenArr );
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false );
			curl_setopt($ch, CURLOPT_COOKIE, '' );
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
			$g = curl_exec($ch);
			curl_close($ch);
			$xml = simplexml_load_string($g);
			$this -> userInfo = array(
				'userID' => explode("@", (string)$xml -> response -> email )[0],
				'nickname' => (string)$xml -> response -> nickname,
				'age' => (string)$xml -> response -> age,
				'birth' => (string)$xml -> response -> birthday,
				'gender' => (string)$xml -> response -> gender,
				'profImg' => (string)$xml -> response -> profile_image
			);
		}
		public function get_userInfo(){
			return $this -> userInfo;
		}
		public function get_userID(){
			return $this -> userInfo['userID'];
		}
		public function get_nickname(){
			return $this -> userInfo['nickname'];
		}
		public function get_age(){
			return $this -> userInfo['age'];
		}
		public function get_birth(){
			return $this -> userInfo['birth'];
		}
		public function get_gender(){
			return $this -> userInfo['gender'];
		}
		public function get_profImg(){
			return $this -> userInfo['profImg'];
		}
	}
?>

파일로 필요한 분은 file download

Usage

사용법은 그냥 단순하다.
먼저 간략하게 필자가 작업한 로그인 처리 단계를 보면 다음과 같다.

  1. 첫 페이지에서 "로그인" 링크를 타고 네이버 로그인 인증 호출 페이지로 이동
  2. 네이버 로그인 인증 호출 페이지에서 네이버 API와 통신
  3. 네이버 API에 의해 callback URL로 넘어오면 이 페이지에서 실제 필요한 로그인 처리

참, 본 코드를 사용하기 위해서는 cURL 확장 모듈이 설치되어 있어야 한다.

네이버 로그인 인증 호출 페이지

네이버 로그인 인증 호출 페이지에서는 단순히 네이버 개발자센터에서 발급받은 ClientID, ClientSecret 키와 등록해둔 Callback URL로 OAuthRequest 인스턴스를 하나 생성한 후, set_state와 request_auth를 차례대로 실행해 주기만 하면 된다.
필자의 경우는 ClientID, ClientSecret, OAuthRequest 값을 미리 변수에 담아두어( $nid_ClientID, $nid_ClientSecret, $nid_RedirectURL ) 사용했다.

<?php
	require '/class.naverOAuth.php';

	$request = new OAuthRequest( $nid_ClientID, $nid_ClientSecret, $nid_RedirectURL );
	$request -> set_state();
	$request -> request_auth();
?>

실제 로그인 처리 페이지

실 로그인 페이지에서도 크게 할 건 없다. ㅋ
네이버 로그인 인증 호출 페이지에서 처럼 OAuthRequest 인스턴스를 하나 생성한 후, call_accesstoken과 get_user_profile를 실행함으로 모든 과정은 끝난다.

<?php
	require '/class.naverOAuth.php';

	$request = new OAuthRequest( $nid_ClientID, $nid_ClientSecret, $nid_RedirectURL );
	$request -> call_accesstoken();
	$request -> get_user_profile();
?>

이렇게 실행을 시키고 나면, 네이버 API로부터 얻어진 사용자 정보는 $userInfo 프로퍼티에 저장(?)이 되게 되고, get_userInfo등의 메서드를 이용하여 필요한 정보를 꺼내어다가 필요한 처리를 하면 된다.

Comment

어제 필받고 짧은 시간에 만들어 놓은 거라… 예외처리가 안되어 있… 쿨럭…
예외처리는 쓰시는 분들이 알아서… 쿨럭… ;;;

변경 점

    네이버 API 측의 요청으로 코드가 아래와 같이 수정되었습니다. (네이버 API 웹서버 패치 과정에서 request에 대한 호환이 맞지 않아 오류 발생)

    ‘Authorization : ‘.$data[‘token_type’].’ ‘.$data[‘access_token’]
    ‘Authorization: ‘.$data[‘token_type’].’ ‘.$data[‘access_token’]
Authored By 멀더끙