##@file # BrowserID authentication backend file ##@class # BrowserID authentication backend class package Lemonldap::NG::Portal::AuthBrowserID; use strict; use Lemonldap::NG::Portal::Simple; use LWP::UserAgent; use HTTP::Request; use JSON; our $VERSION = '1.3.0'; ## @apmethod int authInit() # Enables Browser ID (required for templates) # @return Lemonldap::NG::Portal constant sub authInit { my $self = shift; $self->{browserIdVerificationURL} ||= "https://verifier.login.persona.org/verify"; $self->{browserIdAuthnLevel} = "2" unless defined $self->{browserIdAuthnLevel}; $self->{browserIdSiteName} ||= "LemonLDAP::NG"; $self->{browserIdSiteLogo}; $self->{browserIdBackgroundColor} ||= "#000"; # Enable BrowserID in template $self->{tpl_browserIdEnabled} = 1; # Set BrowserID customization parameters $self->{tpl_browserIdSiteName} = $self->{browserIdSiteName} if $self->{browserIdSiteName}; $self->{tpl_browserIdSiteLogo} = $self->{browserIdSiteLogo} if $self->{browserIdSiteLogo}; $self->{tpl_browserIdBackgroundColor} = $self->{browserIdBackgroundColor} if $self->{browserIdBackgroundColor}; PE_OK; } ## @apmethod int setAuthSessionInfo() # @return Lemonldap::NG::Portal constant sub setAuthSessionInfo { my $self = shift; $self->{sessionInfo}->{_user} = $self->{user}; $self->{sessionInfo}->{authenticationLevel} = $self->{browserIdAuthnLevel}; $self->{sessionInfo}->{_browserIdAnswer} = $self->{browserIdAnswer}; $self->{sessionInfo}->{_browserIdAnswerRaw} = $self->{browserIdAnswerRaw}; PE_OK; } ## @apmethod int extractFormInfo() # Get BrowserID assertion # @return Lemonldap::NG::Portal constant sub extractFormInfo { my $self = shift; # Assertion should be browserIdAssertion parameter if ( $self->{browserIdAssertion} = $self->param('browserIdAssertion') ) { $self->lmLog( "BrowserID Assertion found: " . $self->{browserIdAssertion}, 'debug' ); # Resolve assertion my $ua = new LWP::UserAgent; push @{ $ua->requests_redirectable }, 'POST'; my $postdata = "assertion=" . $self->{browserIdAssertion} . "&audience=" . $self->{portal}; $self->lmLog( "Send $postdata to " . $self->{browserIdVerificationURL}, 'debug' ); my $request = HTTP::Request->new( 'POST' => $self->{browserIdVerificationURL} ); $request->content_type('application/x-www-form-urlencoded'); $request->content($postdata); my $answer = $ua->request($request); $self->lmLog( "Verification response: " . $answer->as_string, 'debug' ); if ( $answer->code() == "200" ) { # Get JSON answser $self->{browserIdAnswerRaw} = $answer->content; $self->lmLog( "Received BrowserID answer: " . $self->{browserIdAnswerRaw}, 'debug' ); my $json = new JSON(); $self->{browserIdAnswer} = $json->decode( $self->{browserIdAnswerRaw} ); if ( $self->{browserIdAnswer}->{status} eq "okay" ) { $self->{user} = $self->{browserIdAnswer}->{email}; $self->lmLog( "Found user " . $self->{user} . " in BrowserID verification answer", 'debug' ); # TODO - check audience # TODO - adjust session duration with BrowserID expires field # TODO - check SSL certificate return PE_OK; } else { if ( $self->{browserIdAnswer}->{reason} ) { $self->lmLog( "Assertion " . $self->{browserIdAssertion} . " verification error: " . $self->{browserIdAnswer}->{reason}, 'error' ); } else { $self->lmLog( "Assertion " . $self->{browserIdAssertion} . " not verified by BrowserID provider", 'error' ); } return PE_ERROR; } } else { $self->lmLog( "Fail to validate BrowserId assertion " . $self->{browserIdAssertion}, 'error' ); return PE_ERROR; } return PE_OK; } # No assertion, return to login page with BrowserID login script $self->{tpl_browserIdLoadLoginScript} = 1; return PE_FIRSTACCESS; } ## @apmethod int authenticate() # Verify assertion and audience # @return Lemonldap::NG::Portal constant sub authenticate { PE_OK; } ## @apmethod int authFinish() # Does nothing. # @return Lemonldap::NG::Portal constant sub authFinish { PE_OK; } ## @apmethod int authLogout() # Call BrowserID logout method # @return Lemonldap::NG::Portal constant sub authLogout { my $self = shift; $self->{tpl_browserIdLoadLogoutScript} = 1; PE_OK; } ## @apmethod boolean authForce() # Does nothing # @return result sub authForce { return 0; } ## @method string getDisplayType # @return display type sub getDisplayType { return "logo"; } 1; __END__ =head1 NAME =encoding utf8 Lemonldap::NG::Portal::AuthBrowserID - Perl extension for building Lemonldap::NG compatible portals with Mozilla BrowserID protocol =head1 SYNOPSIS use Lemonldap::NG::Portal::SharedConf; my $portal = new Lemonldap::NG::Portal::Simple( configStorage => {...}, # See Lemonldap::NG::Portal authentication => 'BrowserID', ); if($portal->process()) { # Write here the menu with CGI methods. This page is displayed ONLY IF # the user was not redirected here. print $portal->header('text/html; charset=utf8'); # DON'T FORGET THIS (see CGI(3)) print "..."; # or redirect the user to the menu print $portal->redirect( -uri => 'https://portal/menu'); } else { print $portal->header('text/html; charset=utf8'); # DON'T FORGET THIS (see CGI(3)) print "