Mojo-SAML

 view release on metacpan or  search on metacpan

lib/Mojolicious/Plugin/SAML.pm  view on Meta::CPAN

package Mojolicious::Plugin::SAML;

use Mojo::Base 'Mojolicious::Plugin';

use Carp ();
use Crypt::OpenSSL::RSA;
use Crypt::OpenSSL::X509;
use Mojo::File 'path';
use Mojo::SAML ':docs';
use Mojo::SAML::IdP;
use Mojo::URL;
use Mojo::Util;
use Scalar::Util ();

has [qw/metadata route/];

sub sp_metadata {
  my $plugin = shift;
  #TODO this should search for an SPSSODescriptor
  return $plugin->metadata->descriptors->[0];
}

sub register {
  my ($plugin, $app, $conf) = @_;
  $conf ||= {};
  $conf = { %$conf, %{$app->config->{SAML}} };
  Carp::croak 'No SAML configuration given'
    unless keys %$conf;

  my $login = $conf->{handle_login} // Carp::croak 'handle_login is required';
  my $key   = Crypt::OpenSSL::RSA->new_private_key(path($conf->{key})->slurp);
  my $cert  = Crypt::OpenSSL::X509->new_from_string(path($conf->{cert})->slurp);
  my $idp   = Mojo::SAML::IdP->new->from($conf->{idp});

  my $location  = $conf->{location};
  my $entity_id = $conf->{entity_id} // $location;

  my $key_info = KeyInfo->new(cert => $cert);
  my $key_desc = KeyDescriptor->new(
    key_info => $key_info,
    use => 'signing',
  );
  my $post = AssertionConsumerService->new(
    index    => 0,
    binding  => 'HTTP-POST',
    location => $location,
  );
  my $redir = AssertionConsumerService->new(
    index    => 1,
    binding  => 'HTTP-Redirect',
    location => $location,
  );
  my $sp = SPSSODescriptor->new(
    key_descriptors => [$key_desc],
    assertion_consumer_services => [$post, $redir],
    nameid_format => [qw/unspecified/],
  );
  my $metadata = EntityDescriptor->new(
    id => 'MOJOSAML_METADATA',
    entity_id => $entity_id,
    descriptors => [$sp],
    insert_signature => Signature->new(key_info => $key_info),
    insert_xml_declaration => 1,
    sign_with_key => $key,
  );
  $plugin->metadata($metadata);

  $app->helper('saml.authn_request' => sub {
    my ($c, %opt) = @_;

    my $binding = $opt{binding} // 'HTTP-Redirect';
    my $passive = $opt{passive} // 0;
    # TODO get "sign" default from idp
    my $sign    = $opt{sign}    // 1;

    my $url = $idp->location_for(SingleSignOnService => $binding);
    my $req = AuthnRequest->new(
      issuer => $entity_id,
      assertion_consumer_service_index => 0,
      is_passive => $passive,
      nameid_policy => NameIDPolicy->new(format => 'unspecified'),
      destination => "$url",
    );

    if ($binding eq 'HTTP-Redirect') {
      $url->query(SAMLRequest => $req->to_string_deflate);

      if ($sign) {
        $url->query({SigAlg => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'});
        $key->use_sha256_hash;
        my $val = $url->query->to_string;



( run in 2.750 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )