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

NAME

LWP::Authen::OAuth2::ServiceProvider - Understand OAuth2 Service Providers

VERSION

Version 0.01

SYNOPSIS

This is a base module for representing an OAuth 2 service provider. It is implicitly constructed from the parameters to LWP::Authen::OAuth2-new>, and is automatically delegated to when needed.

The first way to try to specify the service provider is with the parameters service_provider and possibly flow:

    LWP::Authen::OAuth2->new(
        ...
        service_provider => "Foo",
        flow => "Bar", # optional
        ...
    );

The first parameter will cause LWP::Authen::OAuth2::ServiceProvider to look for either LWP::Authen::OAuth2::ServiceProvider::Foo, or if that is not found, for Foo. (If neither is present, an exception will be thrown.) The second parameter will be passed to that module which can choose to customize the service provider behavior based on the flow.

The other way to specify the service provider is by passing in sufficient parameters to create a custom one on the fly:

    LWP::Authen::OAuth2->new(
        ...
        authorization_endpoint => $authorization_endpoint,
        token_endpoint => $token_endpoint,

        # These are optional but let you get the typo checks of strict mode
        authorization_required_params => [...],
        authorization_more_params => [...],
        ...
    );

See LWP::Authen::OAuth2::Overview if you are uncertain how to figure out the Authorization Endpoint and Token Endpoint from the service provider's documentation.

KNOWN SERVICE PROVIDERS

The following service providers are provided in this distribution, with hopefully useful configuration and documentation:

SUBCLASSING

Support for new service providers can be added with subclasses. To do that it is useful to understand how things get delegated under the hood.

First LWP::Authen::OAuth2 asks LWP::Authen::OAuth2::ServiceProvider to construct a service provider. Based on the service_provider argument, it figures out that it needs to load and use your base class. A service provider will generally support multiple flows with different behaviors. You are free to take the flow and dynamically decide which subclass of yours will be loaded instead. Should your subclass need to, it can decide that that a subclass of LWP::Authen::OAuth2 should be used that actually knows about request types that are specific to your service provider. This should be seldom needed, but things can vary sufficiently that the hook is provided "just in case".

For all of the potential complexity that is supported, most service provider subclasses should be simple. Just state what fields differ from the specification for specific requests and flows, then include documentation. However should you have a crazy service provider, that should still be possible.

Here are the methods that were designed to be useful to override. See the source if you have a need that none of these address. But if you can do what you need to do through these, please do.

authorization_endpoint

Takes no arguments, returns the URL for the Authorization Endpoint for the service provider. Your subclass cannot function without this.

token_endpoint

Takes no arguments, returns the URL for the Token Endpoint for the service provider. Your subclass cannot function without this.

init

Subclasses are found and potentially loaded during new. Therefore you cannot override that. However once an empty object is created of the final class that handles the service provider, the first call is $self-init($opts)> where $opts is a hashref. This is actually called early in LWP::Authen::OAuth2-new(...)> with $opts set to the options passed in there. Thus any parameters that you consume here can override parameters passed there.

To consume options and copy them to $self please use the following interface:

    $self->copy_option($opts, $required_field);
    $self->copy_option($opts, $optional_field, $default);

If you want to consume options and return them as values instead:

    my $value1 = $self->extract_option($opts, $required_field);
    my $value2 = $self->extract_option($opts, $optional_field, $default);

This interface deletes from the hash, so do not try to consume an option twice.

flow_class

Given the name of a flow, returns the class for that flow and service provider. Not required, but useful for service providers with many flows and different arguments. The default is your class.

If you provide this, it is your responsibility to make sure that the required class is loaded.

You also should map the flow default to the most likely default flow that people will want to use. This likely is whatever most closely resembles "webserver application". That way people will be able to use your module without specifying a flow.

required_defaults

The parameters that must be passed into LWP::Authen::OAuth2-new(...)> as defaults for any requests that need them. The default required defaults are client_id and client_secret. In general it is good to only require arguments that are needed to generate refreshed tokens. If your flow does not supply you with a refresh_token, then you should require nothing.

more_defaults

The parameters that can be passed into LWP::Authen::OAuth2-new(...)> as defaults for any requests that need them. The default optional defaults are redirect_uri and scope. Note that there is no harm in having the same arguments be both required and optional, or multiple times in optional.

{authorization,request,refresh}_required_params

These three methods list parameters that must be included in the authorization url, the post to request tokens, and the post to refresh tokens respectively. Supplying these can give better error messages if they are left out.

{authorization,request,refresh}_more_params

These three methods list parameters that can be included in the authorization url, the post to request tokens, and the post to refresh tokens respectively. In strict mode, supplying any parameters not included in more or required params will be an error. Otherwise this has little effect.

{authorization,request,refresh}_default_params

These three methods returns a list of key/value pairs mapping parameters to default values in the authorization url, the post to request tokens, and the post to get refreshed tokens respectively. Supplying these can stop people from having to supply the parameters themselves.

An example where this could be useful is to support a flow that uses different types of requests than normal. For example there are possible requests in the specification with grant_type=password and grant_type=client_credentials that could be substituted for request_tokens with a flow and service provider that supports them.

post_to_token_endpoint

When a post to a token endpoint is constructed, this actually sends the request. The specification allows service providers to require authentication beyond what the specification requires, which may require cookies, specific headers, etc. This method allows you to address that case.

access_token_class

Given a token_type, what class implements access tokens of that type? If your provider creates a new token type, or implements an existing token type in a quirky way that requires a nonstandard model to handle, this method can let you add support for that.

The specification says that all the token_type must be case insensitive, so all types are lower cased for you.

If the return value does not look like a package name, it is assumed to be an error message. As long as you have spaces in your error messages and normal looking class names, this should DWIM.

See LWP::Authen::OAuth2::AccessToken for a description of the interface that your access token class needs to meet. (You do not have to subclass that - just duck typing here.)

oauth2_class

Override this to cause LWP::Authen::OAuth2-new(...)> to return an object in a custom class. This would be appropriate if people using your service provider need methods exposed that are not in LWP::Authen::OAuth2.

Few service provider classes should find a reason to do this, but it can be done if you need.

collect_action_params

This is the method that processes parameters for a given action. Should your service provider support a new kind of request, you can use this along with the *_{required,more,default}_params functions to support it.

The implementation of request_tokens in this module give an example of how to use it.

CONTRIBUTING

Patches contributing new service provider subclasses to this distributions are encouraged. Should you wish to do so, please submit a git pull request that does the following:

  • Implement your provider

    The more completely implemented, the better.

  • Name it properly

    The name should be of the form:

        LWP::Authen::OAuth2::ServiceProvider::$ServiceProvider
  • List it

    It needs to be listed as a known service provider in this module.

  • Test it

    It is impossible to usefully test a service provider module without client secrets. However you can have public tests that it compiles, and private tests that will, if someone supplies the necessary secrets, run fuller tests that all works. See the existing unit tests for examples.

  • Include it

    Your files need to be included in the MANIFEST in the root directory.

  • Document Client Registration

    A developer should be able to read your module and know how to register themselves as a client of the service provider.

  • List Useful Flows

    Please list the flows that the service provider uses, with just enough detail that a developer can figure out which one to use. Listed flows should, of course, also be implemented.

  • Document important quirks

    If the service provider requires or allows useful parameters, try to mention them in your documentation.

  • Document limitations

    If there are known limitations in your implementation, please state them.

  • Link to official documentation

    If the service provider provides official OAuth 2 documentation, please link to it. Ideally a developer will not need to refer to it, but should know how to find it.

AUTHOR

Ben Tilly, <btilly at gmail.com>

BUGS

Please report any bugs or feature requests to bug-lwp-authen-oauth2 at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=LWP-Authen-OAuth2. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc LWP::Authen::OAuth2::ServiceProvider

You can also look for information at:

ACKNOWLEDGEMENTS

Thanks to Rent.com for their generous support in letting me develop and release this module. My thanks also to Nick Wellnhofer <wellnhofer@aevum.de> for Net::Google::Analytics::OAuth2 which was very enlightening while I was trying to figure out the details of how to connect to Google with OAuth2.

LICENSE AND COPYRIGHT

Copyright 2013 Rent.com.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0

Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.

If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.

This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.

This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.

Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.