oauth-2.0 如何使用Mojolicious::Plugin::OAuth2获取刷新令牌

hc8w905p  于 2022-10-31  发布在  其他
关注(0)|答案(1)|浏览(211)

我是Mojolicious::Plugin::OAuth2的快乐用户,但有一个但是:我可以毫无问题地获得访问令牌,但我不知道如何获得刷新令牌。文档有点简洁,我在野外找不到示例。
目前我这样做:

plugin OAuth2 => {
          providers => {
                google => {
                       key    => 'somekey',
                       secret => 'somesecret',
                       redirect => 'http://localhost:3000/login/google',
                       access_type => 'offline',
                       scope => join ' ', qw|some scopes|,
                      }
                   }
         };

get '/' => sub {
    my $c = shift;
    $c->render(template => 'login');
};

get '/done' => sub {
    my $c = shift;
    $c->render(text => 'done: ' . $c->session('token'));
};

get '/login/google' => sub {
    my $c = shift;
    my $otx = $c->render_later->tx;

    my $args = { redirect_uri => 'http://localhost:3000/login/google' };

    $c->oauth2->get_token_p(google => $args)
    ->then(sub {
           my $otx = $otx;
           return unless my $res = shift;
           $c->session(token => $res->{access_token});
           1;
           })
    ->then(sub {
           my $tx = shift;
           my $ua = $c->app->ua;
           my $url = 'https://www.googleapis.com/userinfo/v2/me';
           my $tx = $ua->build_tx(GET => $url);
           $tx->req->headers->authorization('Bearer ' . $c->session('token'));
           return $ua->start_p($tx);
           })
    ->then(sub {
           my $tx = shift;
           my $otx = $otx;

           my $data = $tx->res->json;
           $c->app->log->info($tx->res->body);
           $c->app->log->info(dumper $tx->res->json);
           $c->redirect_to('/done');
           })
    ->catch(sub {
            my $err = shift;
            $c->log->info($err);
            $c->render(text => $err);
        });
};

(很抱歉转储)这几乎是Mojolicious::Plugin::OAuth2的标准流程。
然而,据我所知,来自Google的响应不包含任何刷新令牌,我也不知道如何请求一个令牌--在中间的某个地方插入$c->oauth2->get_refresh_token_p($provider_name => \%args);会给我一个糟糕的请求响应。
那么,我应该如何做才能让它正常工作呢?

1sbrub3j

1sbrub3j1#

如果在创建OAuth2插件示例时设置了access_type => 'offline'(如您在示例中所做的那样),get_access_token_p()将返回刷新标记(除了access_token之外),如here所述。您应该将刷新令牌存储在安全的地方。然后您可以在以后使用它来刷新访问令牌(例如,如果api调用返回访问令牌过期错误)。在这种情况下,您可以使用已存储为参数的刷新令牌调用get_refresh_token_p()
下面是一个例子:

use feature qw(say);
use strict;
use warnings;
use experimental qw(declared_refs refaliasing signatures);
use JSON;
use Mojolicious::Lite -signatures;

{
    my @scopes = ('https://www.googleapis.com/auth/userinfo.email');
    my $cred = read_credentials('credentials.json');
    plugin OAuth2 => {
        providers => {
            google => {
                # "offline": instructs the Google authorization server to also return a
                # refresh token the first time the application exchanges an
                # authorization code for tokens
                access_type  => 'offline',
                key          => $cred->{client_id},
                secret       => $cred->{client_secret},
            }
        }
    };

    #  Note that this /login/google end point callback is called in two different cases:
    #   - the first case is when the user accesses the login page,
    #   - the second case is when the google authorization server redirects back
    #     to the redirect_uri parameter. In this case the "code" query parameter is
    #     set.
    #  The OAuth2 plugin can differentiate between these two cases, such that
    #    get_token_p() will behave correctly for each case..
    #
    get '/login/google' => sub {
        my $c = shift;
        my $app = $c->app;
        my $args = {
            redirect     => 1, # tell get_token_p() to redirect to the current route
            scope        => (join ' ', @scopes),
        };
        $c->oauth2->get_token_p(google => $args)->then(
            # This callback is for the second response from google aut. server,
            #  (the first response is handled by get_token_p() internally)
            sub {
                my $res = shift;   # The response from the authorization server
                $c->session(token => $res->{access_token});
                $c->session(refresh_token => $res->{refresh_token});
                #------------------------------------------
                # This should log the refresh token
                #------------------------------------------
                $c->log->info('Refresh token: ' . $res->{refresh_token});
                #------------------------------------------

                my $ua = $app->ua;
                my $url = 'https://www.googleapis.com/userinfo/v2/me';
                my $tx = $ua->build_tx(GET => $url);
                $tx->req->headers->authorization('Bearer ' . $c->session('token'));
                return $ua->start_p($tx);
            }
        )->then(
            sub {
                my $tx = shift;
                my $data = $tx->res->json;
                #$app->log->info($app->dumper($data));
                $c->session(user_email => $data->{email});
                $c->redirect_to('/done');
            }
        )->catch(
            sub {
                my $err = shift;
                $c->log->info($err);
                $c->render(text => $err);
            }
        );
    };
    get '/google/refreshtoken' => sub {
        my $c = shift;
        my $app = $c->app;
        my $refresh_token = $c->session('refresh_token');
        if ($refresh_token) {
            my $args = {
                refresh_token => $refresh_token,
            };
            $c->oauth2->get_refresh_token_p(google => $args)->then(
                sub {
                    my $res = shift;
                    # update stored access token
                    $c->session(token => $res->{access_token});
                    $c->render(template => 'refreshed');
                }
            );
        }
        else {
            $c->render(text => "No refresh token stored. Please login first");
        }
    };
    get '/' => sub {
        my $c = shift;
        $c->render(template => 'index');
    };
    get '/done' => sub {
        my $c = shift;
        $c->render(template => 'done');
    };
    app->start;
}

sub read_credentials( $fn ) {
    open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";
    my $str = do { local $/; <$fh> };
    close $fh;
    my $cred = decode_json( $str );
    return $cred->{installed};
}

__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html>
  <head><title>Testing mojolicious oauth2 refresh token...</title></head>
  <body>
    <h1>Please access route /login/google to start...</h1>
  </body>
</html>

@@ done.html.ep
<!DOCTYPE html>
<html>
  <head><title>Done testing mojolicious oauth2</title></head>
  <body>
    <h1>Done testing. User email: <%= $c->session('user_email') %></h1>
  </body>
</html>

@@ refreshed.html.ep
<!DOCTYPE html>
<html>
  <head><title>Refreshed token</title></head>
  <body>
    <h1>Refreshed token</h1>
  </body>
</html>

相关问题