Net-MitDK
view release on metacpan or search on metacpan
bin/mitdk-authenticate view on Meta::CPAN
use JSON::XS qw(decode_json);
use Net::MitDK;
$|++;
my $win32_install = (( $ARGV[0] // '' ) eq '--win32-install');
my $port = 9999;
my ($server, $error, $e);
my ($state, $code_challenge, $code_verifier, $nonce);
my $ua = IO::Lambda::HTTP::UserAgent->new;
my $mgr = Net::MitDK::ProfileManager->new;
my $profile = [$mgr->list]->[0];
unless ( defined $profile ) {
print "Creating default profile...";
$profile = 'default';
my ( $ok, $error ) = $mgr->create($profile);
if ( $ok ) {
print "ok\n";
} else {
print "error: $error\n";
exit 1;
}
}
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 setfrom { '<p><a href="/setfrom">Set "From:" email address</a>' }
sub mailcheck { '<p><a href="/testmail">Test MitDK login</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 profile { '<p><a href="/profile">Change profile (' . $profile . ')</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 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 NemID ticket" unless $resp->content =~ /name="(SAMLResponse)" value="(.*?)"/;
my $saml = "$1=" . uri_escape($2);
$resp = $ua->request( HTTP::Request::Common::POST(
'https://gateway.digitalpost.dk/auth/s9/mit-dk-nemlogin/ssoack',
Content => $saml
))->wait;
return error("NemID ticket is received but cannot login. Did you regsiter at <a href='https://mit.dk'>Digital Post</a>?")
unless ($resp->header('Location') // '') =~ m[(com.netcompany.mitdk://nem-callback)\?.*code=([^\&]+)];
$resp = $ua->request( HTTP::Request::Common::POST(
'https://gateway.mit.dk/view/client/authorization/token?'.
'grant_type=authorization_code&'.
"redirect_uri=$1&".
'client_id=view-client-id-mobile-prod-1-id&'.
"code=$2&".
"code_verifier=$code_verifier"
))->wait;
return error("NemID ticket is received but cannot authorize to MitDK") unless
$resp->is_success && $resp->header('Content-Type') eq 'application/json';
my $json;
eval { $json = decode_json( $resp->content ); };
return error("Got bad response from MitDK") unless $json && $json->{access_token};
my ($ok, $error) = Net::MitDK->new(profile => $profile)->first_login($json)->wait;
return error("Cannot get first re-authorization: $error") unless $ok;
return html '<h2>Logged in to MidDK!</h2><p>Now you can start the mitdk server' . mailcheck . setfrom . main . quit;
}
sub params
{
map {
m/^(\w+)=(.*)$/;
my ($k,$v) = ($1,$2);
$v =~ s/%(..)/chr(hex($1))/ge;
($k, $v);
} split '&', $_[0];
}
my %routes;
%routes = (
'/profile' => sub {
my $combo = '';
for ($mgr->list) {
$combo .= "<option value='$_' " .
(($_ eq $profile) ? ' selected="selected"' : '').
">$_</option>\n";
}
html <<PROFILE . main
<h2>Change profile</h2>
<p>
( run in 1.566 second using v1.01-cache-2.11-cpan-4ab04211f4c )