OAuth 2.0 을 이용해 Token을 얻어, 이  토큰으로 REST API를 통한 서버의 자원에 접근해 이용하는 방법은 이제 보편화된 방법 입니다. OAuth.2.0은 대부분 http/1.1을 이용하기 때문에, 대부분은 웹서버라면 다 이용할 수 있습니다.

요즘 많이 사용하게 되어 한 번쯤 정리해 둘 필요가 있을 것 같아 블로그에 글을 남겨 둡니다.

 

[공식 문서]

구글 OAuth 2.0 OverView : https://developers.google.com/identity/protocols/OAuth2?hl=ko .
구글 OAuth 2.0 for Web Server Applications : https://developers.google.com/identity/protocols/OAuth2WebServer?hl=ko .
유저 프로필 요청 : https://developers.google.com/gmail/api/v1/reference/users/getProfile .

 

[주요 사이트]

구글 클라우드 콘솔 : https://console.developers.google.com .

 

 

1. 구글 클라우드 콘솔에 프로젝트 및 Gamil 라이브러리 사용설정.

구글 로그인 기능은 자신의 구글 자원에 접근(구글 드라이브, 메일 등) 하기 위해 처음 로그인 하는 부분만 활용해 로그인 연동 처리를 하는 것 입니다. 그래서 최소 하나의 API를 사용하겠다 라고, 신청을 해야 그 다음 부터 이용이 가능합니다. 우리는 Gmail을 신청할 것 입니다. 구글 사용자라면 누구나, Gmail을 이용하고, 모든 유저가 활성화 되어 있기 때문입니다.

1) 프로젝트 생성.

목적별로 프로젝트를 생성한 후, 그 프로젝트에 Gmail을 생성합니다. 우리는 php google oauth 라는 프로젝트를 만들어 보도록 합시다. 처음 구글에 접속하면 다음과 같은 페이지가 표시될 것 입니다. 여기서 만들기를 눌러 프로젝트 만들기 창으로 접근합니다.

모든 사항을 입력한 후 만들기 버튼을 눌러 줍니다. (프로젝트 아이디를 변경해도 상관없습니다. 하지만 여기서는 하지 않았습니다.).

2) Gmail 라이브러리 추가.

그럼 php google oauth 프로젝트가 자동으로 선택되고 (하나 뿐 이므로), API 라이브러리를 추가하라는 메시지를 만납니다. 여기서 우리가 필요로 하는 Gmail API를 추가 해 보도록 합시다.

그럼.  API 라이브러리를 관리하는 화면이 나오는데, 왼쪽 메뉴의 이메일을 선택해 줍니다.

Gmail API를 선택합니다. (이메일은 Gmail API 하나만 있지만, 다른 카테고리는 여러개의, API가 표시될 수 있습니다.)

Gmail API 상세화면에서 사용설정 버튼을 눌러 사용할 수 있도록 해 줍니다.

 

2. OAuth 2.0 동의 화면 설정.

OAuth 로그인을 하는 과정에서 어떤 권한을 사용하는지등을 나타내는 화면이 표시 됩니다. 이 화면을 표시하기 위한 동의 화면을 구성하도록 하겠습니다.

아래와 같이 OAuth 동의 화면 탭을 선택해 나온 페이지에 애플리케이션 이름을 입력하고, 하단의 저장 버튼을 눌러 줍니다.

 

 

3. OAuth 2.0을 위한 클라이언트 ID 만들기.

구글 클라우드 API를 사용하기 위해서는 반드시 OAuth 2.0 방식을 통한 로그인 후 토큰을 획득해야 합니다. 이 과정에서 프로젝트를 구분하는 클라이언트 ID가 필요한데, 지금 만들어 보도록 하겠습니다.

아래와 같이 사용자 인증 정보 탭을 선택하고, OAuth 클라이언트 ID 생성을 눌러 줍니다.

클라이언트 성성 화면에서, 웹 애플리케이션을 선택하고, 이름은 적당히 입력해 주고, 생성 버튼을 눌러 줍니다.

그리고 나면 OAuth 클라이언트 정보를 확인시켜 주는 창이 뜰 텐데 일단 확인을 누르고 닫아 줍니다.

 

4. 리디렉션 주소 설정.

OAuth 2.0에서는 필연적으로 사용자가 계정의 아이디 및 패스워드를 입력하게 됩니다. 이런 과정을 통해 서버 운영자인 우리는, 사용자의 계정이나 패스워드를 저장할 필요성이 사라집니다. 사용자가 로그인을 무사히 완료하면, 서버쪽으로 성공 결과 및 인증코드를 돌려 주는 데, 그 주소를 리디렉션 주소라고 합니다. 이 값은 중요하므로, 구글에 등록된 주소로만 리디렉션 해 줍니다.

이 과정에서 도메인이 필요한 데, 우리는 테스트 과정이므로, localhost를 사용할 것 입니다. 실제 서비스를 한다면, 도메인을 이용해 구성해 주세요.

저는 웹페이지 중 http://localhost/p999/google_OAuth2/token_exchange.php 라는 주소를 설정할 것 입니다. 파일이름이 token_exchange.php 인 이유는 받은 인증 코드를 이용해 토큰으로 교환할 것 이기 때문입니다.

사용자 인증탭에 OAuth 2.0에 사용할 클라이언트 ID의 수정 버튼을 눌러 줍니다. 그리고, 나온 페이지에 승인된 리디렉션 URI에 리디렉션 주소를 입력하고( 꼭 엔터를 눌러 줍니다. ), 저장 버튼을 눌러 줍니다.

 

5. 인증을 구현을 위한 필요 정보 조사.

인증에 사용할 클라이언트 ID를 눌러 주도록 합니다.

페이지에서 각각의 정보를 수집합니다.

클라이언트 ID : 107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com
크라이언트 보안 비밀 : NaJSvmGdShGg1kkXkGdvHp4T
승인된 리디렉션 URI : http://localhost/p999/google_OAuth2/token_exchange.php

 

6. 인증서 코드 요청 페이지 만들기.

저는 PC의 웹서버에, /p999/google_OAuth2/ 폴더 속에서 웹 파일들을 만들어 주고 있습니다. 코드를 요청하는 페이지 이름은 request_code.php 로 정하고 코딩하였습니다. 이 페이지에서 사용되는 내용은, Using OAuth 2.0 for Web Server Application (위에 링크 있습니다.)공식 문서에 HTTP/REST 부분의, 내용 입니다.

scope 의 권한 부분은 OAuth 2.0 API Scopes ( 링크 )에서 확인할 수 있는 데, 우리는 메일의 모든 권한을 요청할 것 입니다. 그 권한은 ( https://mail.google.com/ ) 입니다.

state 값은 리디렉션 시 그대로 다시 전달 받을 값 입니다. 로그인 과정에서 수많은 유저가 동시에 접속하면, 어떤 유저의 로그인인지 확인할 필요가 있습니다. 이때 사용하면 좋으며, 세션을 사용한다면, 사용하지 않아도 상관이 없습니다.

코드 자체는 로그인할 주소를 입력한 뒤, 리디렉션 해 주면 끝이 납니다. 아래는, request_code.php 페이지의 내용 입니다.

<?php



    // 주요 값들.
    $client_id = "107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com";
    $client_secret = "NaJSvmGdShGg1kkXkGdvHp4T";
    $redirection_url = "http://localhost/p999/google_OAuth2/token_exchange.php";
    $scope = "https://mail.google.com/";

    // 인증 코드 주소 생성.
    $reqAddr = "https://accounts.google.com/o/oauth2/v2/auth"
    . "?client_id=" . $client_id
    . "&redirect_uri=" . urlencode( $redirection_url )
    . "&scope=" . urlencode( $scope )
    . "&state=OK"
    . "&access_type=offline"
    . "&include_granted_scopes=true"
    . "&response_type=code"
    ;

    // 지정한 주소로 리디렉션 시키기.
    header( 'Location: ' . $reqAddr );

?>

인증 코드를 무사히 넘겨 받았을 때 결과를 리디렉션 받아 토큰을 교환하는 페이지의 소스는 일단 아래와 같이 둡니다.

<?php


    print_r( $_GET );

자그럼 request_code.php 파일을 실행해 보도록 합시다. 정상적으로 리디렉션 된다면, 아래와 같은 페이지를 만날 수 있습니다.

일반적으로 구글 로그인 화면이므로, 쉽게 무엇을 넣어야 할 지 알 수 있을 것 입니다. 계속해서 로그인을 하도록 합니다.

로그인이 완료되면, 우리가 scope로 요청했던, Gmail의 모든 권한을 부여해달라는 페이지가 뜹니다. 우리는 현재 우리 계정으로 로그인하지만, 일반 유저가 우리 홈페이지에 접근할 때, 이 페이지를 보개 될 것이며, 자신의 Gmail을 이용할 것을 알게 됩니다. 일단 계속 해서 허용을 해 봅시다.

자 그럼 우리가 설정해 놓은 리디렉션 페이지로 GET을 통해 code, scope, state 값이 넘어 온 것을 확인할 수 있습니다.

 

7. 토큰 교환

우리가 리디렉션 받은 페이지에서, 받은 코드값을 사용해 토큰을 받아 오도록 하겠습니다. 이 부분은 서버에서 POST 통신을 해야 하므로, 약간의 난이도(?)가 있습니다.

POST 통신을 위해, fsocketopen 메소드를 상용하겠습니다. 공식 문서에 보면, 아래와 같이 데이터를 보내 도록 되어 있습니다. 우리도 그 값 그대로 보내면 되겠습니다. 공식 문서에는 endpoint가 https://www.googleapis.com/oauth2/v4/token 라고 되어 있습니다. 여기로 접속할 것 입니다.

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code

아래는 토큰 교환을 하는 코드 입니다.

    // 주요 값들.
    $client_id = "107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com";
    $client_secret = "NaJSvmGdShGg1kkXkGdvHp4T";
    $redirection_url = "http://localhost/p999/google_OAuth2/token_exchange.php";
    $scope = "https://mail.google.com/";



    // 보낼 쿼리 만들기.
    $sendQuery = ""
    . "code=" . $_GET['code']
    . "&client_id=" . $client_id
    . "&client_secret=" . $client_secret
    . "&redirect_uri=" . urlencode( $redirection_url )
    . "&grant_type=authorization_code"
    ;


    // 보낼 데이터를 만들어 주기 위한 기본값을 설정합니다.
    $endline = "\r\n";
    $req = "";
    
    
    // 구글에서 요청한 필수 헤더를 추가합니다.
    $req = "POST /oauth2/v4/token HTTP/1.1" . $endline
    . "Host: www.googleapis.com" . $endline
    . "Content-Type: application/x-www-form-urlencoded" . $endline
    ;

    
    // POST 데이터를 보내기 위한 기본 헤더를 추가힙니다.
    $req .= "Content-Length: " . strlen($sendQuery) . $endline
    . "Connection: Close" . $endline
    ;
    
    // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
    $req .= $endline;
    
    // 내용을 추가해 보내어 줍니다.
    $req .= $sendQuery;
    ;

    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $req );


    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $TokenJson = "";


    // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $TokenJson .= $line;
        }

    }

    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );

    // 받아 온 Token Json을 토큰 객체로 만들어 줍니다.
    $TokenObj = json_decode( $TokenJson );

    // 받아 온 토큰을 출랙해 줍니다.
    echo "받은 토큰은 아래와 같습니다.<br>";
    print_r( $TokenObj );
    echo "<br>---------------------------<br><br>";

 

8. 유저 프로필 요청.

gmail의 간단한 사용자의 정보를 가지고 옵니다. GET 타입이므로, 리디렉션 처리를 하면 간단하지만, 우리는 지금 학습단계이므로, fsockopen 을 이용해 구현해 보도록 하겠습니다.

gmail 의 유저 프로필 요청을 합니다. 공식 문서를 참고해 주시고, 아래 코드를 추가하면 됩니다. 아마 별다른 설명은 필요 없을 것이라 봅니다.

    // 보낼 문장을 반들어 줍니다.
    $userinfor_req = "GET /gmail/v1/users/me/profile HTTP/1.1" . $endline
    . "Authorization: Bearer " . $TokenObj->access_token . $endline
    . "Host: www.googleapis.com" . $endline
    . "Connection: Close" . $endline

    . $endline


    ;


    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $userinfor_req );




    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $profileJson = "";


    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $profileJson .= $line;
        }

    }


    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );



    // 가지고 온 유저 데이터를 출력하도록 합니다.
    echo "확인한 유저 정보는 아래와 같습니다.<br>";
    echo $profileJson;
    

잘 실행되었다면, 아래와 같은 페이지를 볼수 있습니다.

 

 

마지막. 전체 코드 입니다.

<?php



    // 주요 값들.
    $client_id = "107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com";
    $client_secret = "NaJSvmGdShGg1kkXkGdvHp4T";
    $redirection_url = "http://localhost/p999/google_OAuth2/token_exchange.php";
    $scope = "https://www.googleapis.com/auth/gmail.readonly";
    // $scope = "profile|email|openid";
    // $scope = urlencode( $scope );


    // 보낼 쿼리 만들기.
    $sendQuery = ""
    . "code=" . $_GET['code']
    . "&client_id=" . $client_id
    . "&client_secret=" . $client_secret
    . "&redirect_uri=" . urlencode( $redirection_url )
    . "&grant_type=authorization_code"
    ;


    // 보낼 데이터를 만들어 주기 위한 기본값을 설정합니다.
    $endline = "\r\n";
    $req = "";
    
    
    // 구글에서 요청한 필수 헤더를 추가합니다.
    $req = "POST /oauth2/v4/token HTTP/1.1" . $endline
    . "Host: www.googleapis.com" . $endline
    . "Content-Type: application/x-www-form-urlencoded" . $endline
    ;

    
    // POST 데이터를 보내기 위한 기본 헤더를 추가힙니다.
    $req .= "Content-Length: " . strlen($sendQuery) . $endline
    . "Connection: Close" . $endline
    ;
    
    // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
    $req .= $endline;
    
    // 내용을 추가해 보내어 줍니다.
    $req .= $sendQuery;
    ;

    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $req );


    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $TokenJson = "";


    // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $TokenJson .= $line;
        }

    }

    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );

    // 받아 온 Token Json을 토큰 객체로 만들어 줍니다.
    $TokenObj = json_decode( $TokenJson );

    // 받아 온 토큰을 출랙해 줍니다.
    echo "받은 토큰은 아래와 같습니다.<br>";
    print_r( $TokenObj );
    echo "<br>---------------------------<br><br>";





    // [-- 아래는 유저 프로필 가지고 오기 관련 --]

    
    // 보낼 문장을 반들어 줍니다.
    $userinfor_req = "GET /gmail/v1/users/me/profile HTTP/1.1" . $endline
    . "Authorization: Bearer " . $TokenObj->access_token . $endline
    . "Host: www.googleapis.com" . $endline
    . "Connection: Close" . $endline

    . $endline


    ;


    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $userinfor_req );




    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $profileJson = "";


    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $profileJson .= $line;
        }

    }


    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );



    // 가지고 온 유저 데이터를 출력하도록 합니다.
    echo "확인한 유저 정보는 아래와 같습니다.<br>";
    echo $profileJson;
    

 

 

 

 

 

 

 

Posted by 창업닉군
,