The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

OpenID::Lite::RelyingParty - OpenID RelyingParty support module

SYNOPSIS

    my $openid = OpenID::Lite::RelyingParty->new();

    sub login {

        my $self = shift;

        my $user_suplied_identifier = $self->req->param('openid_identifier');
        return unless $self->validate( $user_suplied_identifier );

        my $checkid_request = $openid->begin( $user_suplied_identifier )
            or $self->show_error( $openid->errstr );

        my $sreg = OpenID::Lite::Extension::SREG::Request->new;
        $sreg->request_fields(qw(nickname));

        $checkid_request->add_extension($sreg);

        my $redirect_url = $checkid_request->redirect_url(
            return_to => q{http://example.com/return_to},
            realm     => q{http://example.com/},
        );

        return $self->redirect_to( $redirect_url );
    }

    sub return_to {
        my $self = shift;
        my $openid = OpenID::Lite::RelyingParty->new;

        my $res = $openid->complete( $self->request, q{http://myapp/return_to} );

        if ( $res->is_success ) {

            # openid login successfully completed.
            # you should save the verified identifier.

            my $display_identifier = $res->display_identifier;
            my $identity_url       = $res->identity_url;

        } elsif ( $res->is_canceled ) {

            # user canceled openid-login.
            #
            # redirect back to top-page or login-page.
            return $your_app->redirect('http://yourapp.com/');

        } elsif ( $res->is_setup_needed ) {

            # you requested as immediate-mode.
            # but OP requires setup.

            # so, then redirect to the indicated url
            return $your_app->redirect( $res->url );

            # if you accept OP with white-list,
            # You can know whether the OP accepts immedate mode or not.
            # So, it's better to change not to use immediate-mode.

        } elsif ( $res->is_not_openid ) {

            return $your_app->error('request is not for openid.');

        } elsif ( $res->is_invalid ) {

            # failed to verify returned assertion
            $your_app->log( $res->message );
            $your_app->error('Failed to verify assertion.');

        } elsif ( $res->is_error ) {

            # error response.
            $your_app->log( $res->message );
            $your_app->log( $res->contact );
            $your_app->log( $res->referrence );

            $your_app->error('Got error response from OP');

        }

    }

DESCRIPTION

This module allows you to make OpenID RelyingParty easily. This supports OpenID 2.0. Most of these interface is borrowd from ruby-openid which is provided by OpenID Enabled.

You only have to execute 'begin' and 'complete'. These methods automatically and properly execute each OpenID communication.

But if you want to customize behavior in detail, You alco can use rower API of this module, for example, 'discover', 'associate', 'idres', and so on.

'Lite' means nothing. It's to escape namespace confliction.

PREPARE

You should start with preparing OpenID::Lite::RelyingParty object with defualt set.

    my $openid_rp = OpenID::Lite::RelyingParty->new();

Or set options.

    use OpenID::Lite::Constants::AssocType qw(HMAC_SHA256);
    use OpenID::Lite::Constants::SessionType qw(DH_SHA256);
    my $openid_rp = OpenID::Lite::RelyingParty->new(
        assoc_type   => HMAC_SHA256,
        session_type => DH_SHA256,
        session      => $myapp->session, # HTTP::Session object or other object which has same interface.
        agent        => $agent,
        store        => OpenID::Lite::RelyingParty::Store::Cache->new,
    );

new

assoc_type

HMAC_SHA1 is set by default. OpenID::Lite::Constants::AssocType

session_type

NO_ENCRYPTION is set by default. OpenID::Lite::Constants::SessionType

agent

If you omit, LWP::UserAgent is set by default.

To keep your application secure, you'd better set agent with more-secure one. like LWPx::ParanoidAgent or OpenID::Lite::Agent::Paranoid.

OpenID::Lite::Agent::Dump dump the request and response object for debug.

session

To reduce the cost on 'idres', you can set session object. This object must behaves as same as HTTP:Session object. (Just requires only 'get' and 'set' methods.)

If session is set and discovery is executed by claimed-id, On 'idres' process, it need'nt execute re-discovery to verify returned information.

See Also HTTP::Session

store

This if for saving associations which is established between RP and OP. If this is undef, OpenID process is carried out as 'stateless mode'. In stateless case, check_authentication http request is required to verify signature included in checkid-response.

You had better to set this to reduce networking cost. If your RP site allows OP with white-list (maybe this is a standard way now), Not so much associations are build. So Hash object on memory is enough to store them. OpenID::Lite::RelyingParty::Store::OnMemory fits for this case.

But your site accepts all the OpenID Providers, More assocations will be established with them. Then you may need another solution.

If you omit, OpenID::Lite::RelyingParty::Store::OnMemory is set by default. In future OpenID::Lite::RelyingParty::Store::Cache will be pushed into this package.

See Also OpenID::Lite::RelyingParty::Store::OnMemory

BEGIN

You should

1. normalize user suplied identifier 2. execute discovery with identifier ( if arleady you obtained OP's information, you can omit this phase ) 3. established association with OP ( if stateless mode, this phase is omitted ) 4. make check-id request 5. redirect user to OP's endpoint as checkid-request.

There are methods corresponding to each phase. You can use them or simple 'begin' methods that execute most of these process automatically.

simple API example

    sub login {
        my $your_app = shift;
        my $identifier = $your_app->req->param('openid_identifier');
        # $your_app->validate_identifier( $identifier );

        my $checkid_request = $openid_rp->begin( $identifier )
            or $your_app->show_error( $your_app->openid->errstr );

        my $endpoint_url = $checkid_request->redirect_url(
            return_to => q{http://myapp.com/return_to},
            realm     => q{http://myapp.com/},
        );

        return $your_app->redirect( $endpoint_url );
    }

simple API and limiting OP and reducing discovery-cost.

    use OpenID::Lite::Constants::Namespace qw(SERVER_2_0);

    sub login {
        my $your_app = shift;

        my $service = OpenID::Lite::RelyingParty::Discover::Service->new;
        $service->add_type( SERVER_2_0 );
        $service->add_uri( q{http://example.com/op/endpoint} );

        my $checkid_request = $openid_rp->begin_without_discovery( $service );

        my $endpoint_url = $checkid_request->redirect_url(
            return_to => q{http://myapp.com/return_to},
            realm     => q{http://myapp.com/},
        );

        return $your_app->redirect( $endpoint_url );
    }

raw API example

    sub login {
        my $your_app = shift;
        my $identifier = $your_app->req->param('openid_identifier');
        # $your_app->validate_identifier( $identifier );

        $identifier = $openid_rp->normalize_identifier( $identifier )
            or return $your_app->error( $openid_rp->errstr );

        my $services = $openid_rp->discover( $identifier )
            or return $your_app->error( $openid_rp->errstr );

        unless ( @$services > 0 ) {
            return $your_app->error('No proper OpenID Provider found.');
        }

        my $service = $services->[0];

        my $association = $openid_rp->associate( $services->[0] )
            or return $your_app->error( $openid_rp->errstr );

        $your_app->save_association( $service, $association );
        $your_app->session->set( 'openid.last_requested_endpoint', $service );

        my $checkid_request = OpenID::Lite::RelyingParty::CheckID::Request->new(
            service     => $service,
            association => $association,
        );

        my $endpoint_url = $checkid_request->redirect_url(
            return_to => q{http://myapp.com/return_to},
            realm     => q{http://myapp.com/},
        );

        return $your_app->redirect( $endpoint_url );
    }

normalize_identifier($identifier)

Normalize user suplied identifier, and return OpenID::Lite::Identifier object.

    my $normalized_identifier = $openid_rp->normalized_identifier($user_suplied_identifier)
        or die $openid_rp->errstr;

discover($normalized_identifier)

Do discovery and return found service informations(Array of OpenID::Lite::RelyingParty::Discover::Service)

    $services = $openid_rp->discover( $normalized_identifier )
        or die $openid_rp->errstr;

associate($service)

Establish association with OP. Returns <OpenID::Lite::Association> object.

    my $association = $openid_rp->associate( $service )
        or die $openid_rp->errstr;

begin($user_suplied_identifier)

Return <OpenID::Lite::relyingParty::CheckID::Request> object.

    my $checkid_req = $openid_rp->begin( $identifier )
        or die $openid_rp->errstr;

begin_without_discovery($service)

Return <OpenID::Lite::relyingParty::CheckID::Request> object.

    my $checkid_req = $openid_rp->begin_without_discovery( $service )
        or die $openid_rp->errstr;

APPEND EXTENSION

You can add extension request onto checkid request.

    my $chekid_req = $openid_rp->begin(...);

    my $sreg_req = OpenID::Lite::Extension::SREG::Request->new;
    $sreg_req->request_field('nickname');
    $sreg_req->request_field('fullname');
    $checkid_req->add_extension( $sreg_req );

    my $ui_req = OpenID::Lite::Extension::UI::Request->new;
    $ui_req->mode('popup');
    $checkid_req->add_extension( $ui_req );

    my $url = $checkid_req->redirect_url(
        ...
    );

COMPLETE

When OP redirect back user to the return_to url you defined on checkid-request, you should execute 'idres'. You can choose row API and simple wrapper here too.

row API example

    sub complete {
        my $your_app = shift;

        my $params = OpenID::Lite::Message->from_request( $your_app->request );
        my $service = $your_app->session->get('openid.last_requested_endpoint');
        my $association = $your_app->load_association_for( $service );

        my $res = $openid_rp->idres(
            service     => $service,
            association => $association,
            params      => $params,
            current_url => q{http://yourapp.com/return_to},
        );

        if ( $res->is_success ) {

            # openid login successfully completed.
            # you should save the verified identifier.

            my $display_identifier = $res->display_identifier;
            my $identity_url       = $res->identity_url;

        } elsif ( $res->is_canceled ) {

            # user canceled openid-login.
            #
            # redirect back to top-page or login-page.
            return $your_app->redirect('http://yourapp.com/');

        } elsif ( $res->is_setup_needed ) {

            # you requested as immediate-mode.
            # but OP requires setup.

            # so, then redirect to the indicated url
            return $your_app->redirect( $res->url );

            # if you accept OP with white-list,
            # You can know whether the OP accepts immedate mode or not.
            # So, it's better to change not to use immediate-mode.

        } elsif ( $res->is_not_openid ) {

            return $your_app->error('request is not for openid.');

        } elsif ( $res->is_invalid ) {

            # failed to verify returned assertion
            $your_app->log( $res->message );
            $your_app->error('Failed to verify assertion.');

        } elsif ( $res->is_error ) {

            # error response.
            $your_app->log( $res->message );
            $your_app->log( $res->contact );
            $your_app->log( $res->referrence );

            $your_app->error('Got error response from OP');

        }

    }

simple API example

    sub complete {
        my $your_app = shift;

        my $current_url = q{http://yourapp.com/return_to};
        my $res = $openid_rp->complete( $your_app->request, $current_url );

        # same as row API example above
        if ( $res->is_success ) {
            ...
        } elsif ( $res->is_canceled ) {
            ...
        } elsif ( $res->is_setup_needed ) {
            ...
        } elsif ( $res->is_not_openid ) {
            ...
        } elsif ( $res->is_invalid ) {
            ...
        } elsif ( $res->is_error ) {
            ...
        }

    }

idres(%args)

Returns OpenID::Lite::RelyingParty::CheckID::Result object.

params(required)

OpenID::Lite::Message object. You should encode your request to OpenID::Lite::Message.

current_url(required)

URL string that represents the endpoint you indicate as return_to on checkid-request.

services(optional)
association(optional)

complete($request, $current_url)

Returns OpenID::Lite::RelyingParty::CheckID::Result object.

EXTRACT EXTENSION RESPONSE

In successfull response, You can extract extension data you requested.

    } elsif ( $res->is_success ) {

        my $sreg_res = OpenID::Lite::Extension::SREG::Response->from_success_response($res);
        my $data = $sreg_res->data;
        my $nickname = $data->{nickname}||'';
        my $fullname = $data->{fullname}||'';
        ...
    }

SEE ALSO

http://openid.net/specs/openid-authentication-2_0.html http://openidenabled.com/

TODO

Improve an interoperability with majour services.

AUTHOR

Lyo Kato, <lyo.kato@gmail.com>

COPYRIGHT AND LICENSE

Copyright (C) 2009 by Lyo Kato

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 324:

Unknown directive: =over4

Around line 326:

'=item' outside of any '=over'