package YTMusicAPI::Auth::OAuth::OAuthCredentials; use strict; use warnings; use JSON; use LWP::UserAgent; use Data::Dumper; use YTMusicAPI::Constants qw( OAUTH_CLIENT_ID OAUTH_CLIENT_SECRET OAUTH_CODE_URL OAUTH_SCOPE OAUTH_TOKEN_URL OAUTH_USER_AGENT ); sub new { my ( $class, $args ) = @_; unless ( defined $args->{client_id} == defined $args->{client_secret} ) { die "OAuthCredential init failure. " . "Provide both client_id and client_secret or neither."; } my $self = {}; bless $self, $class; $self->{client_id} = $args->{client_id} ? $args->{client_id} : OAUTH_CLIENT_ID; $self->{client_secret} = $args->{client_secret} ? $args->{client_secret} : OAUTH_CLIENT_SECRET; $self->{_session} = $args->{session} ? $args->{session} : LWP::UserAgent->new; if ( $args->{proxies} ) { @$self->{_session}{ keys %{ $args->{proxies} } } = values %{ $args->{proxies} }; } return $self; } sub get_code { my ($self) = @_; my $code_response = $self->_send_request( OAUTH_CODE_URL, { "scope" => OAUTH_SCOPE } ); return decode_json( $code_response->decoded_content ); } sub _send_request { my ( $self, $url, $data ) = @_; $data->{"client_id"} = $self->{client_id}; my $request = HTTP::Request->new( POST => $url ); $request->header( "User-Agent" => OAUTH_USER_AGENT ); $request->content( encode_json($data) ); my $response = $self->{_session}->request($request); if ( $response->code == 401 ) { my $data = $response->decode_json; my $issue = $data->{"error"}; if ( $issue eq "unauthorized_client" ) { die "Token refresh error. Most likely client/token mismatch."; } elsif ( $issue eq "invalid_client" ) { die "OAuth client failure. Most likely client_id and client_secret " . "mismatch or YouTubeData API is not enabled."; } else { die "OAuth request error. status_code: " . $response->code . ", url: $url, content: " . Dumper($data); } } return $response; } sub token_from_code { my ( $self, $device_code ) = @_; my $response = $self->_send_request( OAUTH_TOKEN_URL, { "client_secret" => $self->{client_secret}, "grant_type" => "http://oauth.net/grant_type/device/1.0", "code" => $device_code, } ); return decode_json( $response->decoded_content ); } sub refresh_token { my ( $self, $refresh_token ) = @_; my $response = $self->_send_request( OAUTH_TOKEN_URL, { "client_secret" => $self->{client_secret}, "grant_type" => "refresh_token", "refresh_token" => $refresh_token, } ); return decode_json( $response->decoded_content ); } 1;