sironekotoroの日記

Perl で楽をしたい

Mojolicious::Lite で Google Drive API のアクセストークンを表示する

そろそろ前に進まないとと思いつつ

ここしばらく、Google の提供する API を扱うためのことやってきたんですが、やりつつ一つの疑念がありました。

途中で Web にアクセスして URL を貼り付けるところ、そのまま HTTP の GET で良いのでは?

いちいちブラウザに貼り付けなくても良さそうな・・・?

というか、最初からブラウザで動かしたら?

それって、 Perl 入学式 でやった Mojolicious::Lite で実装できそう・・・と言うことで実装してみました。

下準備としては、Google Compute Platform の API 設定の OAuth 同意画面で、アプリケーションのホームページを http://localhost:3000 にしておくことです。

言わずと知れた Mojolicious を morbo で起動した時の URL とデフォルトポートですね。

f:id:sironekotoro:20201104192003p:plain

出来上がったもの

苦労したところ

Google の認証画面に遷移するところまでは割とすんなり行きました。

問題はそっから ACCESS_TOKEN と REFRESH_TOKEN つくるところで詰まりました。

結局、 Net::Google::OAuth のソースを参考にしつつ作ることに・・・

おかげで、メールアドレスやリダイレクトのURLをURLエスケープするとか、HTTP::Tiny の POST つかって JSON で送るとかを読み取ることができました。

コード

Google Drive API での利用を想定して SCOPE のところがそうなってます。

http://localhost:3000 -> (POST) -> Google の認証画面 -> (GET) -> http://localhost:3000/code という感じで遷移します。

URL の組み立てのところが雑だなぁ・・・と思うんですが、まぁそこはそれってことで。

#!/usr/bin/env perl
use Mojolicious::Lite;
use HTTP::Tiny;
use JSON;
use URI::Escape;
use URI;

my ( $CLIENT_ID, $CLIENT_SECRET, );

get '/' => sub {
    my $c = shift;

    $c->render( template => 'index' );
};

get '/code' => sub {
    my $c = shift;

    my $code = $c->param('code');

    # Exchange code to token
    my $param = {
        'client_id'     => $CLIENT_ID,
        'client_secret' => $CLIENT_SECRET,
        'redirect_uri'  => 'http://localhost:3000/code/',
        'code'          => $code,
        'grant_type'    => 'authorization_code',
        'access_type'   => 'offline',
    };

    my $json = encode_json($param);

    my $ht       = HTTP::Tiny->new();
    my $response = $ht->request(
        'POST',
        'https://accounts.google.com/o/oauth2/token',
        {
            headers =>
                { 'Content-Type' => 'application/json; charset=utf-8', },
            content => $json,
        },
    );

    $json = decode_json( $response->{content} );

    $c->stash(
        access_token  => $json->{access_token},
        refresh_token => $json->{refresh_token}
    );

    $c->render( template => 'code' );
};

post '/post' =>
    sub {
    my $c = shift;
    $CLIENT_ID     = $c->param('CLIENT_ID');
    $CLIENT_SECRET = $c->param('CLIENT_SECRET');
    my $MAIL  = $c->param('MAIL');
    my $SCOPE = $c->param('SCOPE');

    my $redirect_url = uri_escape('http://localhost:3000/code/');
    my $login_hint   = uri_escape($MAIL);
    my $scope        = uri_escape($SCOPE);

    # url組み立て
    my $url
        = "https://accounts.google.com/o/oauth2/v2/auth?client_id=$CLIENT_ID&redirect_uri=$redirect_url&scope=$scope&response_type=code&approval_prompt=force&access_type=offline";

    $c->redirect_to($url);

    };

app->start;
__DATA__

@@ index.html.ep
% layout 'default';
% title 'Welcome';
<h1>Welcome to the Mojolicious real-time web framework!</h1>

<form action="/post" method="post">
    <ul>
        <li>CLIENT_ID: <input name="CLIENT_ID" size=80 type="text" value=""></li>
        <li>CLIENT_SECRET: <input name="CLIENT_SECRET" size=80 type="text" value=""></li>
        <li>MAIL: <input name="MAIL" size=20 type="text" value=""></li>
        <li>SCOPE: <input name="SCOPE" size=40 type="text" value="https://www.googleapis.com/auth/drive"></li>
    </ul>
    <input type="submit" value="POSTで投稿する">
</form>


@@ code.html.ep
% layout 'default';
% title 'Welcome';
<h1>Welcome to the Mojolicious real-time web framework!</h1>

<ul>
    <% if ( stash('access_token') )  {%>
        <li>ACCESS TOKEN:<%= $access_token %></li>
    <% } %></textbox>

    <% if ( stash('refresh_token') )  {%>
        <li>REFRESH TOKEN:<%= $refresh_token %></li>
    <% } %>
</ul>

<a href="http://localhost:3000">トップページ</a>

@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
  <head><title><%= title %></title></head>
  <body><%= content %></body>
</html>