The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
NAME

    Plack::Auth::SSO - role for Single Sign On (SSO) authentication

IMPLEMENTATIONS

    * SSO for Central Authentication System (CAS): Plack::Auth::SSO::CAS

    * SSO for ORCID: Plack::Auth::SSO::ORCID

    * SSO for Shibboleth: Plack::Auth::SSO::Shibboleth

SYNOPSIS

        package MySSOAuth;
    
        use Moo;
        use Data::Util qw(:check);
    
        with "Plack::Auth::SSO";
    
        sub to_app {
    
            my $self = shift;
    
            sub {
    
                my $env = shift;
                my $request = Plack::Request->new($env);
                my $session = Plack::Session->new($env);
    
                #did this app already authenticate you?
                #implementation of Plack::Auth::SSO should write hash to session key,
                #configured by "session_key"
                my $auth_sso = $self->get_auth_sso($session);
    
                #already authenticated: what are you doing here?
                if( is_hash_ref($auth_sso) ){
    
                    return [ 302, [ Location => $self->uri_for($self->authorization_path) ], [] ];
    
                }
    
                #not authenticated: do your internal work
                #..
    
                #everything ok: set auth_sso
                $self->set_auth_sso(
                    $session,
                    {
                        package => __PACKAGE__,
                        package_id => $self->id,
                        response => {
                            content => "Long response from external SSO application",
                            content_type => "text/xml"
                        },
                        uid => "<uid>",
                        info => {
                            attr1 => "attr1",
                            attr2 => "attr2"
                        },
                        extra => {
                            field1 => "field1"
                        }
                    }
                );
    
                #redirect to other application for authorization:
                return [ 302, [ Location => $self->uri_for($self->authorization_path) ], [] ];
    
            };
        }
    
        1;
    
    
        #in your app.psgi
    
        builder {
    
            mount "/auth/myssoauth" => MySSOAuth->new(
    
                session_key => "auth_sso",
                authorization_path => "/auth/myssoauth/callback",
                uri_base => "http://localhost:5001"
    
            )->to_app;
    
            mount "/auth/myssoauth/callback" => sub {
    
                my $env = shift;
                my $session = Plack::Session->new($env);
                my $auth_sso = $session->get("auth_sso");
    
                #not authenticated yet
                unless($auth_sso){
    
                    return [ 403, ["Content-Type" => "text/html"], ["forbidden"] ];
    
                }
    
                #process auth_sso (white list, roles ..)
    
                [ 200, ["Content-Type" => "text/html"], ["logged in!"] ];
    
            };
    
        };

DESCRIPTION

    This is a Moo::Role for all Single Sign On Authentication packages. It
    requires to_app method, that returns a valid Plack application

    An implementation is expected is to do all communication with the
    external SSO application (e.g. CAS). When it succeeds, it should save
    the response from the external service in the session, and redirect to
    the authorization url (see below).

    The authorization route must pick up the response from the session, and
    log the user in.

    This package requires you to use Plack Sessions.

CONFIG

    session_key

      When authentication succeeds, the implementation saves the response
      from the SSO application in this session key, together with extra
      information.

      The response should look like this:

          {
              package => "<package-name>",
              package_id => "<package-id>",
              response => {
                  content => "Long response from external SSO application like CAS",
                  content_type => "<mime-type>"
              },
              uid => "<uid-in-external-app>",
              info => {
                  attr1 => "attr1",
                  attr2 => "attr2"
              },
              extra => {
                  field1 => "field1"
              }
          }

      This is usefull for several reasons:

          * the authorization application can distinguish between authenticated and not authenticated users
      
          * it can pick up the saved response from the session
      
          * it can lookup a user in an internal database, matching on the provided "uid" from the external service.
      
          * the key "package" tells which package authenticated the user; so the application can do an appropriate lookup based on this information.
      
          * the key "package_id" defaults to the package name, but is configurable. This is usefull when you have several external services of the same type,
            and your application wants to distinguish between them.
      
          * the original response is stored as text, along with the content type.
      
          * other attributes stored in the hash reference "info". It is up to the implementing package whether it should only used attributes as pushed during
            the authentication step (like in CAS), or do an extra lookup.
      
          * "extra" should be used to store request information.
              e.g. "ORCID" gives a "token".
              e.g. "Shibboleth" supplies the "Shib-Identity-Provider".

    authorization_path

      (internal) path of the authorization route. This path will be
      prepended by "uri_base" to create the full url.

      When authentication succeeds, this application should redirect you
      here

    uri_for( path )

      method that prepends your path with "uri_base".

    id

      identifier of the authentication module. Defaults to the package
      name. This is handy when using multiple SSO instances, and you need
      to known exactly which package authenticated the user.

      This is stored in "auth_sso" as "package_id".

    uri_base

      base url of the Plack application

METHODS

 to_app

    returns a Plack application

    This must be implemented by subclasses

 get_auth_sso($plack_session)

    get saved SSO response from your session

 set_auth_sso($plack_session,$hash)

    save SSO response to your session

    $hash should be a hash ref, and look like this:

        {
            package => __PACKAGE__,
            package_id => __PACKAGE__ ,
            response => {
                content => "Long response from external SSO application like CAS",
                content_type => "<mime-type>",
            },
            uid => "<uid>",
            info => {},
            extra => {}
        }

EXAMPLES

    See examples/app1:

        #copy example config to required location
        $ cp examples/catmandu.yml.example examples/catmandu.yml
    
        #edit config
        $ vim examples/catmandu.yml
    
        #start plack application
        plackup examples/app1.pl

AUTHOR

    Nicolas Franck, <nicolas.franck at ugent.be>

LICENSE AND COPYRIGHT

    This program is free software; you can redistribute it and/or modify it
    under the terms of either: the GNU General Public License as published
    by the Free Software Foundation; or the Artistic License.

    See http://dev.perl.org/licenses/ for more information.

SEE ALSO

    Plack::Auth::SSO::CAS, Plack::Auth::SSO::ORCID
    Plack::Auth::SSO::Shibboleth