File Coverage

blib/lib/EveOnline/SSO.pm
Criterion Covered Total %
statement 34 77 44.1
branch 6 20 30.0
condition 2 3 66.6
subroutine 12 14 85.7
pod 3 3 100.0
total 57 117 48.7


line stmt bran cond sub pod time code
1              
2             =encoding utf-8
3              
4             =head1 NAME
5              
6             EveOnline::SSO - Module for Single Sign On in EveOnline API-services.
7              
8             =head1 SYNOPSIS
9              
10             use EveOnline::SSO;
11              
12             my $sso = EveOnline::SSO->new(client_id => '03ed7324fe4f455', client_secret => 'bgHejXdYo0YJf9NnYs');
13            
14             # return url for open in browser
15             print $sso->get_code();
16             # or
17             print $sso->get_code(state => 'some_ids_or_flags');
18             # or
19             print $sso->get_code(state => 'some_ids_or_flags', scope=>'esi-calendar.respond_calendar_events.v1 esi-location.read_location.v1');
20              
21             # return hash with access and refresh tokens by auth code
22             print Dumper $sso->get_token(code=>'tCaVozogf45ttk-Fb71DeEFcSYJXnCHjhGy');
23             # or hash with access and refresh tokens by refresh_token
24             print Dumper $sso->get_token(refresh_token=>'berF1ZVu_bkt2ud1JzuqmjFkpafSkobqdso');
25            
26             # return hash with access and refresh tokens through listening light web-server
27             print Dumper $sso->get_token_through_webserver(
28             scope=>'esi-calendar.respond_calendar_events.v1 esi-location.read_location.v1',
29             state=> 'Awesome'
30             );
31              
32              
33             =head1 DESCRIPTION
34              
35             EveOnline::SSO is a perl module for get auth in https://eveonline.com through Single Sign-On (OAuth) interface.
36              
37             =cut
38              
39             package EveOnline::SSO;
40 2     2   1847 use 5.008001;
  2         11  
41 2     2   1228 use utf8;
  2         29  
  2         11  
42 2     2   980 use Modern::Perl;
  2         20327  
  2         15  
43 2     2   1118 use JSON::XS;
  2         3741  
  2         91  
44 2     2   826 use URI::Escape;
  2         2512  
  2         95  
45 2     2   837 use MIME::Base64;
  2         1018  
  2         91  
46 2     2   743 use URI::URL;
  2         11275  
  2         120  
47              
48 2     2   1189 use LWP::UserAgent;
  2         68907  
  2         70  
49 2     2   953 use LWP::Socket;
  2         16682  
  2         75  
50              
51 2     2   1028 use Moo;
  2         18140  
  2         12  
52              
53             our $VERSION = "0.03";
54              
55              
56             has 'ua' => (
57             is => 'ro',
58             default => sub {
59             my $ua = LWP::UserAgent->new();
60             $ua->agent( 'EveOnline::SSO Perl Client' );
61             $ua->timeout( 120 );
62             return $ua;
63             }
64             );
65              
66             has 'auth_url' => (
67             is => 'ro',
68             default => 'https://login.eveonline.com/oauth/authorize/',
69              
70             );
71              
72             has 'token_url' => (
73             is => 'ro',
74             default => 'https://login.eveonline.com/oauth/token',
75             );
76              
77             has 'callback_url' => (
78             is => 'rw',
79             default => 'http://localhost:10707/',
80             );
81              
82             has 'client_id' => (
83             is => 'rw',
84             required => 1,
85             );
86              
87             has 'client_secret' => (
88             is => 'rw',
89             required => 1,
90             );
91              
92             has 'demo' => (
93             is => 'rw'
94             );
95              
96             =head1 CONSTRUCTOR
97              
98             =over
99              
100             =item B
101              
102             Require two arguments: client_id and client_secret.
103             Optional arguments: callback_url. Default is http://localhost:10707/
104              
105             Get your client_id and client_secret on EveOnline developers page:
106             L
107              
108             =back
109              
110             =head1 METHODS
111              
112             =over
113              
114             =item B
115              
116             Return URL for open in browser.
117              
118             Optional params: state, scope
119              
120             See available scopes on L
121              
122             # return url for open in browser
123             print $sso->get_code();
124            
125             # or
126             print $sso->get_code(state => 'some_ids_or_flags');
127            
128             # or
129             print $sso->get_code(scope=>'esi-calendar.respond_calendar_events.v1 esi-location.read_location.v1');
130              
131             =back
132             =cut
133              
134             sub get_code {
135 4     4 1 477 my ( $self, %params ) = @_;
136              
137             return $self->auth_url .
138             "?response_type=code&client_id=".$self->client_id .
139             "&redirect_uri=".uri_escape( $self->callback_url ) .
140             ( ( defined $params{scope} ) ? "&scope=" . uri_escape( $params{scope} ) : '' ) .
141 4 100       30 ( ( defined $params{state} ) ? "&state=" . uri_escape( $params{state} ) : '' );
    100          
142             }
143              
144             =over
145              
146             =item B
147              
148             Return hashref with access and refresh tokens.
149             refresh_token is undef if code was received without scopes.
150              
151             Need "code" or "refresh_token" in arguments.
152            
153             # return hash with access and refresh tokens by auth code
154             print Dumper $sso->get_token(code=>'tCaVozogf45ttk-Fb71DeEFcSYJXnCHjhGy');
155            
156             # or hash with access and refresh tokens by refresh_token
157             print Dumper $sso->get_token(refresh_token=>'berF1ZVu_bkt2ud1JzuqmjFkpafSkobqdso');
158              
159             =back
160             =cut
161              
162             sub get_token {
163 2     2 1 11 my ( $self, %params ) = @_;
164              
165 2 50 66     9 return unless $params{code} || $params{refresh_token};
166              
167 2 50       55 return JSON::XS::decode_json( $self->demo ) if $self->demo;
168              
169 0           my $base64 = encode_base64($self->client_id.':'.$self->client_secret,"");
170 0           $self->ua->default_header('Authorization' => "Basic " . $base64 );
171 0           $self->ua->default_header('Content-Type' => "application/x-www-form-urlencoded");
172              
173 0           my $post_params = {};
174 0           foreach my $key ( keys %params ) {
175 0           $post_params->{$key} = $params{$key};
176             }
177              
178             my $res = $self->ua->post($self->token_url, {
179             %$post_params,
180 0 0         grant_type => $params{code} ? 'authorization_code' : 'refresh_token',
181             });
182              
183 0           return JSON::XS::decode_json( $res->content );
184             }
185              
186             =over
187              
188             =item B
189              
190             Return hashref with access and refresh tokens by using local webserver for get code.
191             Use callback_url parameter for start private web server on host and port in callback url.
192              
193             Default url: http://localhost:10707/
194              
195             # return hash with access and refresh tokens
196             print Dumper $sso->get_token_through_webserver(scope=>'esi-location.read_location.v1');
197              
198             =back
199             =cut
200              
201             sub get_token_through_webserver {
202 0     0 1   my ( $self, %params ) = @_;
203              
204 0           my $url = $self->get_code( %params );
205              
206 0 0         if ( $url ) {
207              
208 0           say "Go to url: " . $url;
209 0           my $code = $self->_webserver();
210              
211 0 0         if ( $code ) {
212 0           say $code;
213 0           return $self->get_token(code=>$code);
214             }
215             }
216 0           return;
217             }
218              
219             sub _webserver {
220 0     0     my ( $self, %params ) = @_;
221              
222 0           my $headers = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
223            
224 0           my $conn = new URI::URL $self->callback_url;
225              
226 0           my $sock = new LWP::Socket();
227 0 0         die "Can't bind a socket" unless $sock->bind($conn->host, $conn->port);
228 0           $sock->listen(1);
229              
230 0           my $code;
231 0           while ( my $socket = $sock->accept(1) ) {
232 0           my $content = "EveOnline::SSO code receiver
";
233 0           my $request = '';
234 0           $socket->read( \$request );
235 0 0         if ( $request =~ /code=/g ) {
236 0 0         if ( $request =~ /code=([\w-]+)/ ) {
237 0           $code = $1;
238 0           $request =~ s/GET \/\?([^ ]*) HTTP.+/$1/s;
239 0           $request =~ s/&/
/g;
240 0           $request =~ s/=/:/g;
241              
242 0           $content .= $request;
243 0           $content .= "
Now you can close this page
Fly safe!";
244 0           $socket->write( $headers . $content );
245 0           $socket->shutdown();
246 0           $socket = undef;
247 0           last;
248             }
249             }
250             }
251            
252 0           $sock->shutdown();
253 0           $sock = undef;
254 0           return $code;
255             }
256              
257             1;
258             __END__