NAME
OpenID::Lite::Provider - OpenID Provider support module
SYNOPSIS
OpenID Controller
package YourApp::OpenIDController;
my $op = OpenID::Lite::Provider->new(
endpoint_url => q{http://yourapp.com/openid},
setup_url => q{http://yourapp.com/setup},
server_secret => q{SECRETKEY},
);
# server endpoint
sub openid {
my $your_app = shift;
my $result = $op->handle_request( $your_app->request );
if ( !$result ) {
# error occured
# invalid as openid-request.
$your_app->view->content_type('text/plain');
$your_app->view->content($op->errstr);
return;
} elsif ( $result->is_for_setup ) {
# save the parameters into session
# this is just an example, you can take other ways.
# for example, use query-string parameter.
$your_app->session->set( 'openid.checkid' => $result );
# required setup and
# show decision page
# Case 1. redirect to action that is for setup
$your_app->redirect_to( $your_app->uri_to( action => 'setup' ) );
return;
# Case 2. or directly show setup page.
$your_app->view->render('decision_page', {
realm => $result->get_realm(),
} );
} elsif ( $result->requires_setup ) {
# RP requested as immediate-mode, but your app (provider)
# doesn't accept immediate mode.
return $your_app->redirect_to( $result->make_setup_url() );
} elsif ( $result->is_positive_assertion ) {
# successfully done as immediate-mode.
# execute extension processes here if you need.
my $sreg_req = OpenID::Lite::Extension::SREG::Request->from_provider_response($result);
my $user_data = $self->session->get('user');
my $sreg_data = {
nickname => $user_data->nickname,
fullname => $user_data->fullname,
email => $user_data->email,
};
my $sreg_res = OpenID::Lite::Extension::SREG::Response->extract_response($sreg_req, $sreg_data);
$result->add_extension( $sreg_res );
# redirect back to RP with successful signed params.
return $self->redirect_to( $result->make_signed_url() );
} elsif ( $result->is_for_direct_communication ) {
# direct communication response
# This case is for establishing association and checking auth.
$self->view->content( $result->content );
return;
} elsif ( $result->is_checkid_error ) {
return $self->redirect_to( $self->make_error_url() );
}
}
# action that shows decision page.
sub setup {
my $self = shift;
my $checkid_result = $self->session->get('openid.checkid');
}
# if user canceled to approve RP request.
sub user_cancel {
my $self = shift;
my $checkid_result = $self->session->get('openid.checkid');
return $self->redirect_to( $checkid_result->make_cancel_url() );
}
# if user approved RP request.
sub user_approved {
my $self = shift;
my $checkid_result = $self->session->get('openid.checkid')
or return $self->show_error('Invalid openid-session');
# RETURN POSITIVE ASSERTION
# redirect to RP as positive-assertion
# execute extension processes here if you need.
my $sreg_req = OpenID::Lite::Extension::SREG::Request->from_provider_response($checkid_result);
my $user_data = $self->session->get('user');
my $sreg_data = {
nickname => $user_data->nickname,
fullname => $user_data->fullname,
email => $user_data->email,
};
my $sreg_res = OpenID::Lite::Extension::SREG::Response->extract_response($sreg_req, $sreg_data);
$checkid_result->add_extension( $sreg_res );
return $self->redirect_to( $checkid_result->make_signed_url() );
}
1;
Application Root
package YourApp::RootController;
sub root {
my $self = shift;
if ( $self->req->header('Accept') =~ m!application/xrds+xml!i ) {
print_xrds();
return;
}
}
1;
User Page
package YourApp::UserController;
sub user {
my ( $self, $user_id ) = @_;
if ( $self->req->header('Accept') =~ m!application/xrds+xml!i ) {
print_claimed_id_xrds($user_id);
return;
}
}
1;
DESCRIPTION
This moduel allows you to mae OpenID Provider easily. This supports OpenID 2.0.
'Lite' means nothing. It's to escape namespace confliction.
SETUP
my $op = OpenID::Lite::Provider->new(
endpoint_url => q{http://yourapp.com/openid},
setup_url => q{http://yourapp.com/setup},
);
new
parameters
- setup_url
-
The OpenID setup url.
- endpoint_url
-
The OpenID endpoint url.
- server_secret
-
Secret string to generate association.
- secret_lifetime
-
Lifetime seconds for each association.
- agent
-
Used for RP discovery.
See LWP::UserAgent, LWPx::ParanoidAgent OpenID::Lite::Agent::Dump, OpenID::Lite::Agent::Paranoid
Callback functions You can set callbacks, then they will be able to automatically controll to judge approve request from RP or not. If you want to manually handle request, see 'REQUEST HANDLING' section.
- get_user
-
Callback function to get current user object. Other callback functions uses the returned user object.
my $your_app = ...; get_user => sub { return $your_app->session->get('user'); }
- get_identity
-
Callback function to get user identity. If your app provieds users multiple identifier for each realm, use the second arg.
get_identity => sub { my ( $user, $realm ) = @_; # if your app provides users with only single identifier return $user->get_identity(); # if your app provides users with diffirent identifier for each realm. return $user->get_identity_for($realm); }
- is_identity
-
Callback function that checks the passed identity is for indicated user's one or not. If your app provieds users multiple identifier for each realm, use the third arg.
is_identity => sub { my ( $user, $identity, $realm ) = @_; return ( $user->get_identity_for($realm) eq $identity ) ? 1 : 0; }
- is_trusted
-
Callback function that checks that if the current user trusts requesting RP or not.
is_trusted => sub { my ( $user, $realm ) = @_; return $user->trust( $realm ) ? 1 : 0; }
REQUEST HANDLING
execute handle_reuqest method, and switch process properly for each result type.
my $result = $op->handle_request( $your_app->request );
if ( !$result ) {
# error
} elsif ( ... ) {
} elsif ( ... ) {
} elsif ( ... ) {
}
NOT FOUND RESULT
If $op->handle_request returns nothing. You can pick the error string from $op->errstr method.
if ( !$result ) {
$your_app->log( $op->errstr );
$your_app->show_error( q{ Invalid openid request.} );
}
POSITIVE ASSERTION
When OP accept the case like that, user had already approved the requesting RP, returns positive assertion directly without displaying dicision page. To accomplish this, you have to set callback functions(get_user, get_identity, and so on) when calling 'new' method.
} elsif ( $result->is_positive_assertion ) {
$your_app->redirect( $result->make_signed_url() );
} ...
And if you need support extension. Do their process here, or SETUP phase discribed bellow.
} elsif ( $result->is_positive_assertion ) {
my $sreg_req = OpenID::Lite::Extension::SREG::Request->from_provider_response( $result );
my $user_data = $self->session->get('user');
my $sreg_data = {
nickname => $user_data->nickname,
fullname => $user_data->fullname,
email => $user_data->email,
};
my $sreg_res = OpenID::Lite::Extension::SREG::Response->extract_response($sreg_req, $sreg_data);
$result->add_extension( $sreg_res );
$your_app->redirect( $result->make_signed_url() );
} ...
SETUP
When RP requests checkid not-immediate request, and no error found.( for the case if error found, see CHECKID ERROR section ).
Normally, you can choose two ways here, Show dicision page directly, or redirect user to setup-url.
And to show some information to users. You can pick them from result object. see get_relam, get_claimed_id, get_identity methods bellow.
It is better to save result information into session until user will be back with setup completion action or canceling action. In those actions, result object will be required.
And you can set identifier for user here with 'set_identity' method of result object.
1. Redirecting case.
} elsif ( $result->is_for_setup ) {
$your_app->session->save( 'openid' => $result );
$your_app->redirect( $result->make_setup_url() );
# or manually make url by yourself.
#$your_app->redirect( $your_app->uri_for(
# action => 'setup',
#) );
}
2. Show dicision page case.
} elsif ( $result->is_for_setup ) {
my $realm = $result->get_realm();
# if you set get_identity callback to Provider object,
# you may get identity by this method.
my $identity = $result->get_identity();
# or set manually here.
# my $identity = $your_app->build_user_identity(
# $your_app->session->get('user')->id );
# $result->set_identity( $identity );
$your_app->session->save( 'openid' => $result );
$your_app->show_dicition_page(
realm => $realm,
identity => $identity,
);
} ...
And as described on POSITIVE ASSERTION phase, You can extract information for extension.
} elsif ( $result->is_for_setup ) {
my $realm = $result->get_realm();
# if you set get_identity callback to Provider object,
# you may get identity by this method.
my $identity = $result->get_identity();
# or set manually here.
# my $identity = $your_app->build_user_identity(
# $your_app->session->get('user')->id );
# $result->set_identity( $identity );
$your_app->session->save( 'openid' => $result );
my $sreg_req = OpenID::Lite::Extension::SREG::Request->from_provider_response( $result );
my $fields = $sreg_req->all_requested_fields();
my $message = '';
if ( @$fields > 0 ) {
$message = sprintf(q{the RP requests your fields, "%s"},
join(', ', @$fields) );
}
my $template = 'decision_page.tt';
my $ui_req = OpenID::Lite::Extension::UI::Request->from_provider_response( $result );
if ( $ui_req->mode eq 'popup' ) {
$template = 'decision_page_for_popup.tt';
}
$your_app->show_dicition_page(
template => $template,
realm => $realm,
identity => $identity,
message => $message,
);
} ...
REQUIRES SETUP
RP send checkid-request but OP doesn't accept immedate mode. OP should let RP know setup-url.
} elsif ( $result->requires_setup ) {
$your_app->redirect( $result->make_setup_url() );
} ...
DIRECT COMMUNICATION
For establishing association or CheckAuth request. Directly print key-value form encoded content.
} elsif ( $result->is_direct_communication ) {
$your_app->view->content( $result->content );
} ...
CHECKID ERROR
If any error occured while processing checkid-request, You should redirect user back to RP with openid-error parameters.
} elsif ( $result->is_checkid_error ) {
$your_app->redirect( $result->make_error_url() );
} ...
OP INITIATE
execute discovery and find return_to url by realm. But it works only when RP implements XRDS publishing correctly for realm.
my $assertion = $op->make_op_initiated_assertion(
$rp_realm,
$current_user_identifier,
) or $your_app->error( $op->errstr );
Or if you already know the return_to url corresponding to the realm. You can make assertion without discovery.
my $assertion = $op->make_op_initiated_assertion_without_discovery(
$rp_realm,
$rp_return_to,
$current_user_identifier,
) or $your_app->error( $op->errstr );
If you need, add extension here
my $ext_res = OpenID::Lite::Extension::Something::Response->new;
$ext_res->add_some_param( foo => 'bar' );
$assertion->add_extension( $ext_res );
And finally, build signed url to redirect with it.
$your_app->redirect( $assertion->make_signed_url() );
SEE ALSO
http://openid.net/specs/openid-authentication-2_0.html http://openidenabled.com/
TODO
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.