如何使用Mojolicus::Plugin::OAuth2获取刷新令牌



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

目前我这样做:

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);
});
};

(很抱歉转储(,这几乎是Mojoliquisit::Plugin::OAuth2的标准流。

然而,据我所见,谷歌的响应并不包含任何刷新令牌,我也不知道如何请求刷新令牌——在中间插入$c->oauth2->get_refresh_token_p($provider_name => %args);会给我一个糟糕的请求响应。

那么,我应该怎么做才能正常工作呢?

如果在创建OAuth2插件实例时设置了access_type => 'offline'(就像您在示例中所做的那样(,get_access_token_p()将返回刷新令牌(除了access_token之外(,如下所述。您应该将刷新令牌存储在安全的地方。然后,您可以在以后使用它来刷新访问令牌(例如,如果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>

最新更新