Search code examples
perlntlmmojolicious

How can I use NTLM or Kerberos authentication with Mojo::UserAgent


I am trying to get Mojo::UserAgent to authenticate via NTLM. Rougly like this:

use Mojo::UserAgent;
use Mojo::URL;
use Data::Dump qw/dump/;
use Path::Tiny;
use Authen::NTLM;

$\ = "\n"; $|++;

my $ntlm = Authen::NTLM-> new(host => "some.hidden.pl", user => 'foo',
              domain   => "bar", password => "baz", version  => 2);

my $xml = path($ARGV[0])->slurp;

my $ua = Mojo::UserAgent->new;
my $url = Mojo::URL->new('https://some.hidden.pl/ews/exchange.asmx');

$url->userinfo(sprintf('%s\%s:%s', qw/bar foo baz/));

my $tx = $ua->get($url);

my $tx = $ua->build_tx(GET => $url);
$challenge = $ntlm->challenge;
$tx->req->headers->header('Authorization' => 'NTLM ' . $challenge);
$ua->start($tx);

$challenge = [ split /,\s*/, $tx->res->headers->header('www-authenticate') ]->[0] =~ s/NTLM //r;
$challenge = $ntlm->challenge($challenge);
my $tx = $ua->build_tx(GET => $url);
$tx->req->headers->header('Authorization' => 'NTLM ' . $challenge);
$ua->start($tx);

$tx = $ua->build_tx(POST => $url, {'Content-Type' => 'text/xml'}, $xml );
$tx->req->headers->content_type('text/xml');
$tx->req->headers->header('Authorization' => 'NTLM ' . $challenge);
$ua->start($tx);
print dump $tx->res;

but I keep getting a 401 at the second response from the server.

What am I getting wrong? And would it be easier to use Kerberos authentication (if so, how)?

thanks


Solution

  • I just published a new module that should be pretty helpful in this respect Mojolicious::Plugin::SPNEGO. It is pretty simple to use:

    use Mojolicious::Lite;
    
    my $SERVER = 'my-ad-server.example.com';
    
    app->secrets(['My secret passphrase here']);
    
    plugin 'SPNEGO', ad_server => $SERVER;
    
    get '/' => sub {
       my $c = shift;
       if (not $c->session('user')){
           $c->ntlm_auth({
               auth_success_cb => sub {
                   my $c = shift;
                   my $user = shift;
                   my $ldap = shift; # bound Net::LDAP::SPNEGO connection
                   $c->session('user',$user->{samaccountname});
                   $c->session('name',$user->{displayname});
                   my $groups = $ldap->get_ad_groups($user->{samaccountname});
                   $c->session('groups',[ sort keys %$groups]);
                   return 1;
               }
           }) or return;
       }
    } => 'index';
    
    app->start;
    
    __DATA__
    
    @@ index.html.ep
    <!DOCTYPE html>
    <html>
    <head>
    <title>NTLM Auth Test</title>
    </head>
    <body>
    <h1>Hello <%= session 'name' %></h1>
    <div>Your account '<%= session 'user' %>' belongs to the following groups:</div>
    <ul>
    % for my $group (@{session 'groups' }) {
       <li>'<%= $group %>'</li>
    % }
    </ul>
    </body>
    </html>
    

    The module is based on the also newly released Net::LDAP::SPNEGO module which provides the basic buildingblocks for the SPNEGO dialog.