AnyEvent-Yubico

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Revision history for Perl extension AnyEvent::Yubico.

0.9.3  Wed Mar 06 11:31:03 2013
	- Replaced usage of AnyEvent->timer for timeouts with setting
	  the timeout parameter for each HTTP connection, as the timer
	  was showing some incorrect behavior.

0.9.2  Fri Feb 22 14:09:06 2013
	- Fixed bug preventing verify from working.
	- Lowered the required version of AnyEvent::HTTP to ensure that
	  all dependencies are available on Ubuntu 12.04

0.9.1  Thu Feb 21 15:38:02 2013
	- Added dependencies to Makefile.PL
	- Added COPYING
	- Made tests requiring Internet access optional
		Disable by setting the environment variable NO_INTERNET

0.9.0  Wed Feb 20 12:57:23 2013

lib/AnyEvent/Yubico.pm  view on Meta::CPAN


our $VERSION = '0.9.3';

# Creates a new Yubico instance to be used for validation of OTPs.
sub new {
	my $class = shift;
	my $self = {
		sign_request => 1,
		local_timeout => 30.0,
		urls => [
			"https://api.yubico.com/wsapi/2.0/verify",
			"https://api2.yubico.com/wsapi/2.0/verify",
			"https://api3.yubico.com/wsapi/2.0/verify",
			"https://api4.yubico.com/wsapi/2.0/verify",
			"https://api5.yubico.com/wsapi/2.0/verify"
		]
	};

	my $options = shift;

	$self = { %$self, %$options };

	return bless $self, $class;
};

# Verifies the given OTP and returns a true value if the OTP could be 
# verified, false otherwise.
sub verify {
	return verify_async(@_)->recv->{status} eq 'OK';
}

# Verifies the given OTP and returns a hash containing the server response.
sub verify_sync {
	return verify_async(@_)->recv
}

# Non-blocking version of verify_sync, which returns a condition variable
# (see AnyEvent->condvar for details).
sub verify_async {
	my($self, $otp, $callback) = @_;

	my $nonce = create_UUID_as_string(UUID_V4);
	$nonce =~ s/-//g;

	my $params = {
		id => $self->{client_id},
		nonce => $nonce,
		otp => $otp
	};

lib/AnyEvent/Yubico.pm  view on Meta::CPAN

Though AnyEvent is used internally, the module does not impose any particular
coding style on the caller. Provides both blocking and non-blocking methods of 
OTP verification.

=head1 SYNOPSIS

  use AnyEvent::Yubico;
  
  $yk = AnyEvent::Yubico->new({ client_id => 4711, api_key => '<your API key here>' });

  $result = $yk->verify('<YubiKey OTP here>');
  if($result) ...

For more details about the response, instead call verify_sync($otp), which 
returns a hash containing all the parameters that were in the response.

  $result_details = $yk->verify_sync('<YubiKey OTP here>');
  if($result_details->{status} == 'OK') ...


As an alternative, you can call verify_async, which will return a condition 
variable immediately. This can be used if your application already uses an 
asynchronous model. You can also pass a callback as a second parameter to 
verify as well as verify_async, which will be invoked once validation has
completed, with the result.

  $result_cv = $yk->verify_async('<YubiKey OTP here>', sub {
      #Callback invoked when verification is done
      $result_details = shift;
      if($result_details->{status} eq 'OK') ...
  });
  
  #Wait for the result (blocking, same as calling verify directly).
  $result_details = $result_cv->recv;

=head1 DESCRIPTION

Validates a YubiKey OTP (One Time Password) using the YKVAL 2.0 protocol as 
defined here: https://github.com/Yubico/yubikey-val/wiki/ValidationProtocolV20

To use this module, an API key is required, which can be requested here:
https://upgrade.yubico.com/getapikey/

When creating the AnyEvent::Yubico instance, the following arguments can be passed:

=over 4

=item client_id = $id_int

Required. The client ID corresponding to the API key.

=item api_key => $api_key_string

Optional. The API key used to sign requests and verify responses. Without 
this response signatures won't be verified.

=item urls => $array_of_urls

Optional. Defines which validation server URLs to query. The default uses 
the public YubiCloud validation servers. Must support version 2.0 of the 
validation protocol.

Example:

  $yk = AnyEvent::Yubico->new({
      client_id => ...,
      api_key => ...,
      urls => [
          "http://example.com/wsapi/2.0/verify",
          "http://127.0.0.1/wsapi/2.0/verify"
      ]
  });

=item sign_requests => $enable

Optional. When enabled (enabled by default) requests will be signed, as long 
as api_key is also provided.

=item timeout => $seconds

lib/AnyEvent/Yubico.pm  view on Meta::CPAN

Optional. Security level parameter sent to the server, see the protocol 
details for more information.

=item timestamp => $enable

Optional. When enabled, sends the timestamp parameter to the server, causing
YubiKey counter and timestamp information to be returned in the response.

=item local_timeout => $seconds

Optional. Sets the local timeout for how long the verify method will wait 
until failing. The default is 30 seconds.

=back

=head1 SEE ALSO

The Yubico Validation Protocol 2.0 specification:
https://github.com/Yubico/yubikey-val/wiki/ValidationProtocolV20

More information about the YubiKey:

t/AnyEvent-Yubico.t  view on Meta::CPAN


my $test_signature = "k7ZRKLOn3C6565YVqmG2rd4PHVU=";

ok(defined($validator) && ref $validator eq "AnyEvent::Yubico", "new() works");

is($validator->sign($test_params), $test_signature, "sign() works");

my $default_urls = $validator->{urls};
$validator->{urls} = [ "http://127.0.0.1:0" ];

is($validator->verify_async("vvgnkjjhndihvgdftlubvujrhtjnllfjneneugijhfll")->recv()->{status}, "Connection refused", "invalid URL");

$validator->{urls} = $default_urls;
$validator->{local_timeout} = 0.01;

is($validator->verify_sync("vvgnkjjhndihvgdftlubvujrhtjnllfjneneugijhfll")->{status}, "Connection timed out", "timeout");

$validator->{local_timeout} = 30.0;

subtest 'Tests that require access to the Internet' => sub {
	if(exists($ENV{'NO_INTERNET'})) {
		plan skip_all => 'Internet tests';
	} else {
		plan tests => 5;
	}

	is($validator->verify_sync("ccccccbhjkbulvkhvfuhlltctnjtgrvjuvcllliufiht")->{status}, "REPLAYED_OTP", "replayed OTP");

	$validator = AnyEvent::Yubico->new({
		client_id => $client_id,
	});

	my $result = $validator->verify_sync("ccccccbhjkbubrbnrtifbiuhevinenrhtlckuctjjuuu");

	is($result->{status}, "BAD_OTP", "invalid OTP");

	#Test manual signature verification
	ok(exists($result->{h}), "signature exists");
	my $sig = $result->{h};
	delete $result->{h};
	$validator->{api_key} = $api_key;
	is($validator->sign($result), $sig, "signature is correct");

	ok(! $validator->verify("ccccccbhjkbubrbnrtifbiuhevinenrhtlckuctjjuuu"), "verify(\$bad_otp)");
};



( run in 0.638 second using v1.01-cache-2.11-cpan-5467b0d2c73 )