Net-Eboks

 view release on metacpan or  search on metacpan

bin/eboks-auth-mitid  view on Meta::CPAN

use URI::Escape;
use HTTP::Request;
use HTTP::Request::Common;
use HTTP::Response;
use MIME::Base64 qw(encode_base64url);
use Digest::SHA qw(sha256);
use JSON::XS qw(decode_json encode_json);
use Net::Eboks;


$|++;

my $win32_install = (( $ARGV[0] // '' ) eq '--win32-install');
my $port = 9999;
my ($server, $error);
my ($state, $code_challenge, $code_verifier, $nonce);
my $ua = IO::Lambda::HTTP::UserAgent->new;
my $e  = Net::Eboks->new;

sub socket_check
{
	return IO::Socket::INET-> new(
		PeerAddr => '127.0.0.1',
		PeerPort => shift,
		Proto    => 'tcp',
	);
}

sub randstr($) { encode_base64url(join('', map { chr rand(255) } 1..$_[0])) }
sub init_oauth
{
	$state          = randstr(23);
	$nonce          = randstr(93);
	$code_verifier  = randstr(93);
	$code_challenge = encode_base64url(sha256($code_verifier));
}

sub mailcheck { '<p><a href="/testmail">Test MitDK login'.(($e->{cpr} =~ /^0+$/) ? '' : " for CPR $e->{cpr}").'</a>'  }
sub quit      { '<p><a href="/abort">Quit the wizard</a><p>' }
sub main      { '<p><a href="/">Go back to the start</a><p>' }

sub html($)
{
	my $html = $_[0];
	$html = "<html><body>$html</body></html>";
	HTTP::Response->new( 200, "OK", [
		'Content-Type'   => 'text/html',
		'Content-Length' => length($html),
	], $html)
}

sub pop3
{
	return IO::Socket::INET-> new(
		PeerAddr => '127.0.0.1',
		PeerPort => 8110,
		Proto    => 'tcp',
	);
}

sub h2($)      { html "<h2>$_[0]</h2>" . main . quit }
sub h2x($$)    { html "<h2>$_[0]</h2><p>$_[1]" . main . quit } 
sub error($)   { h2x( 'Error', $_[0] ) }

sub handle_saml
{
	my $resp = shift;

	return error "Cannot get MitID ticket's SAMLResponse" unless $resp->content =~ /name="(SAMLResponse)" value="(.*?)"/;
	my $saml = "$1=" . uri_escape($2);
	return error "Cannot get MitID ticket's RelayState" unless $resp->content =~ /name="(RelayState)" value="(.*?)"/;
	my $rest = "$1=" . uri_escape($2);

	$resp = $ua->request( HTTP::Request::Common::POST(
		'https://gateway.digitalpost.dk/auth/s9/e-boks-nemlogin/ssoack',
		Content => "$rest&$saml",
	))->wait;
	return error("MitID ticket is received but cannot login. Did you register at <a href='https://mit.dk'>Digital Post</a>?")
		unless ($resp->header('Location') // '') =~ m[(eboksdk://ngdpoidc/callback)\?.*code=([^\&]+)];

	my ( $uri, $code ) = ($1, $2);
	my $git_guardian_is_too_smart =
		'ZS1ib2tzLW1vYmls'.
		'ZTp5MHZLUktvVnZx'.
		'TyVOM0hCREswVDVi'.
		'Ynpxb19lWnNJMA';
	$resp = $ua->request( HTTP::Request::Common::POST(
		'https://digitalpost.dk/auth/oauth/token?'.
			'grant_type=authorization_code&'.
			"redirect_uri=$uri&".
			'client_id=e-boks-mobile&'.
			"code=$code&".
			"code_verifier=$code_verifier",
		Authorization => "Basic $git_guardian_is_too_smart==",
	))->wait;
	return error("MitID ticket is received but cannot authorize to Eboks") unless
		$resp->is_success && $resp->header('Content-Type') =~ m[application/json];
	my $json;
	eval { $json = decode_json( $resp->content ); };
	return error("Got bad response from Digitalpost") unless $json && $json->{access_token};

	my $bearer = $json->{access_token};
	$resp = $ua->request( HTTP::Request::Common::POST(
		'https://digitalpostproxy.e-boks.dk/loginservice/v2/connect/usertoken',
		'X-Operation-ID' => 'LoginService_UserToken',
		Authorization    => "Bearer $bearer",
		'Content-Type'   => 'application/json-patch+json',
	))->wait;
	return error("MitID ticket is received but cannot get login token") unless
		$resp->is_success && $resp->header('Content-Type') =~ m[application/json];
	undef $json;
	eval { $json = decode_json( $resp->content ); };
	return error("Got bad response from login service") unless $json && $json->{userToken};

	$resp = $ua->request( HTTP::Request::Common::POST(
		'https://oauth-dk.e-boks.com/1/connect/token', [
			usertoken     => $json->{userToken},
			grant_type    => 'usertoken',
			scope         => 'mobileapi offline_access',
			client_id     => 'MobileApp-Short-Custom-id',
			client_secret => ''.reverse('5FzjwwYeM6WNEamQ'),



( run in 1.570 second using v1.01-cache-2.11-cpan-13bb782fe5a )