
    K                        S r SSKJr  SSKrSSKrSSKrSSKr SSKJr  SSKJ	r	  SSKJ
r
  SSKJr  SSKJr  SS	KJr  SS
KJr  SSKrSSKJs  Jr  SSKJr  SSKJr  SSKJr  SrSrSrSrSrS r " S S\ 5      r!g! \ a	    \" S5      ef = f)a  Utilities for the Flask web framework

Provides a Flask extension that makes using OAuth2 web server flow easier.
The extension includes views that handle the entire auth flow and a
``@required`` decorator to automatically ensure that user credentials are
available.


Configuration
=============

To configure, you'll need a set of OAuth2 web application credentials from the
`Google Developer's Console <https://console.developers.google.com/project/_/apiui/credential>`__.

.. code-block:: python

    from oauth2client.contrib.flask_util import UserOAuth2

    app = Flask(__name__)

    app.config['SECRET_KEY'] = 'your-secret-key'

    app.config['GOOGLE_OAUTH2_CLIENT_SECRETS_FILE'] = 'client_secrets.json'

    # or, specify the client id and secret separately
    app.config['GOOGLE_OAUTH2_CLIENT_ID'] = 'your-client-id'
    app.config['GOOGLE_OAUTH2_CLIENT_SECRET'] = 'your-client-secret'

    oauth2 = UserOAuth2(app)


Usage
=====

Once configured, you can use the :meth:`UserOAuth2.required` decorator to
ensure that credentials are available within a view.

.. code-block:: python
   :emphasize-lines: 3,7,10

    # Note that app.route should be the outermost decorator.
    @app.route('/needs_credentials')
    @oauth2.required
    def example():
        # http is authorized with the user's credentials and can be used
        # to make http calls.
        http = oauth2.http()

        # Or, you can access the credentials directly
        credentials = oauth2.credentials

If you want credentials to be optional for a view, you can leave the decorator
off and use :meth:`UserOAuth2.has_credentials` to check.

.. code-block:: python
   :emphasize-lines: 3

    @app.route('/optional')
    def optional():
        if oauth2.has_credentials():
            return 'Credentials found!'
        else:
            return 'No credentials!'


When credentials are available, you can use :attr:`UserOAuth2.email` and
:attr:`UserOAuth2.user_id` to access information from the `ID Token
<https://developers.google.com/identity/protocols/OpenIDConnect?hl=en>`__, if
available.

.. code-block:: python
   :emphasize-lines: 4

    @app.route('/info')
    @oauth2.required
    def info():
        return "Hello, {} ({})".format(oauth2.email, oauth2.user_id)


URLs & Trigging Authorization
=============================

The extension will add two new routes to your application:

    * ``"oauth2.authorize"`` -> ``/oauth2authorize``
    * ``"oauth2.callback"`` -> ``/oauth2callback``

When configuring your OAuth2 credentials on the Google Developer's Console, be
sure to add ``http[s]://[your-app-url]/oauth2callback`` as an authorized
callback url.

Typically you don't not need to use these routes directly, just be sure to
decorate any views that require credentials with ``@oauth2.required``. If
needed, you can trigger authorization at any time by redirecting the user
to the URL returned by :meth:`UserOAuth2.authorize_url`.

.. code-block:: python
   :emphasize-lines: 3

    @app.route('/login')
    def login():
        return oauth2.authorize_url("/")


Incremental Auth
================

This extension also supports `Incremental Auth <https://developers.google.com/identity/protocols/OAuth2WebServer?hl=en#incrementalAuth>`__. To enable it,
configure the extension with ``include_granted_scopes``.

.. code-block:: python

    oauth2 = UserOAuth2(app, include_granted_scopes=True)

Then specify any additional scopes needed on the decorator, for example:

.. code-block:: python
   :emphasize-lines: 2,7

    @app.route('/drive')
    @oauth2.required(scopes=["https://www.googleapis.com/auth/drive"])
    def requires_drive():
        ...

    @app.route('/calendar')
    @oauth2.required(scopes=["https://www.googleapis.com/auth/calendar"])
    def requires_calendar():
        ...

The decorator will ensure that the the user has authorized all specified scopes
before allowing them to access the view, and will also ensure that credentials
do not lose any previously authorized scopes.


Storage
=======

By default, the extension uses a Flask session-based storage solution. This
means that credentials are only available for the duration of a session. It
also means that with Flask's default configuration, the credentials will be
visible in the session cookie. It's highly recommended to use database-backed
session and to use https whenever handling user credentials.

If you need the credentials to be available longer than a user session or
available outside of a request context, you will need to implement your own
:class:`oauth2client.Storage`.
    wrapsN)	Blueprint)_app_ctx_stack)current_app)redirect)request)sessionurl_forz/The flask utilities require flask 0.9 or newer.)client)clientsecrets)dictionary_storagez'jonwayne@google.com (Jon Wayne Parrott))emailgoogle_oauth2_credentialszgoogle_oauth2_flow_{0}google_oauth2_csrf_tokenc                     [         R                  " [        R                  U 5      S5      nUc  g[        R
                  " U5      $ )zVRetrieves the flow instance associated with a given CSRF token from
the Flask session.N)r
   pop	_FLOW_KEYformatpickleloads)
csrf_tokenflow_pickles     2lib/third_party/oauth2client/contrib/flask_util.py_get_flow_for_tokenr      s<     ++$d,K ||K((    c                       \ rS rSrSrSS jr   SS jrS rS rSS jr	S	 r
S
 rS r\S 5       rS r\S 5       r\S 5       rS rSS jrS rSrg)
UserOAuth2   a  Flask extension for making OAuth 2.0 easier.

Configuration values:

    * ``GOOGLE_OAUTH2_CLIENT_SECRETS_FILE`` path to a client secrets json
      file, obtained from the credentials screen in the Google Developers
      console.
    * ``GOOGLE_OAUTH2_CLIENT_ID`` the oauth2 credentials' client ID. This
      is only needed if ``GOOGLE_OAUTH2_CLIENT_SECRETS_FILE`` is not
      specified.
    * ``GOOGLE_OAUTH2_CLIENT_SECRET`` the oauth2 credentials' client
      secret. This is only needed if ``GOOGLE_OAUTH2_CLIENT_SECRETS_FILE``
      is not specified.

If app is specified, all arguments will be passed along to init_app.

If no app is specified, then you should call init_app in your application
factory to finish initialization.
Nc                 D    Xl         Ub  U R                  " U/UQ70 UD6  g g N)appinit_app)selfr#   argskwargss       r   __init__UserOAuth2.__init__   s'    ?MM#/// r   c                 (   Xl         X`l        Xl        Uc  [        R                  " [
        [        S9nXpl        Uc   UR                  R                  S[        5      nX l        U R                  X4U5        UR                  U R                  5       5        g)a  Initialize this extension for the given app.

Arguments:
    app: A Flask application.
    scopes: Optional list of scopes to authorize.
    client_secrets_file: Path to a file containing client secrets. You
        can also specify the GOOGLE_OAUTH2_CLIENT_SECRETS_FILE config
        value.
    client_id: If not specifying a client secrets file, specify the
        OAuth2 client id. You can also specify the
        GOOGLE_OAUTH2_CLIENT_ID config value. You must also provide a
        client secret.
    client_secret: The OAuth2 client secret. You can also specify the
        GOOGLE_OAUTH2_CLIENT_SECRET config value.
    authorize_callback: A function that is executed after successful
        user authorization.
    storage: A oauth2client.client.Storage subclass for storing the
        credentials. By default, this is a Flask session based storage.
    kwargs: Any additional args are passed along to the Flow
        constructor.
N)keyGOOGLE_OAUTH2_SCOPES)r#   authorize_callbackflow_kwargsr   DictionaryStorager
   _CREDENTIALS_KEYstorageconfigget_DEFAULT_SCOPESscopes_load_configregister_blueprint_create_blueprint)	r%   r#   r5   client_secrets_file	client_idclient_secretr-   r1   r'   s	            r   r$   UserOAuth2.init_app   s|    0 "4!?(::-/G>ZZ^^$:OLF--Ht5578r   c                    U(       a  U(       a  X#sU l         U l        gU(       a  U R                  U5        gSU R                  R                  ;   a)  U R                  U R                  R                  S   5        g U R                  R                  S   U R                  R                  S   sU l         U l        g! [
         a    [        S5      ef = f)a`  Loads oauth2 configuration in order of priority.

Priority:
    1. Config passed to the constructor or init_app.
    2. Config passed via the GOOGLE_OAUTH2_CLIENT_SECRETS_FILE app
       config.
    3. Config passed via the GOOGLE_OAUTH2_CLIENT_ID and
       GOOGLE_OAUTH2_CLIENT_SECRET app config.

Raises:
    ValueError if no config could be found.
N!GOOGLE_OAUTH2_CLIENT_SECRETS_FILEGOOGLE_OAUTH2_CLIENT_IDGOOGLE_OAUTH2_CLIENT_SECRETzOAuth2 configuration could not be found. Either specify the client_secrets_file or client_id and client_secret or set the app configuration variables GOOGLE_OAUTH2_CLIENT_SECRETS_FILE or GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET.)r:   r;   _load_client_secretsr#   r2   KeyError
ValueError)r%   r9   r:   r;   s       r   r6   UserOAuth2._load_config  s     1:.DND.%%&9:.$((//A%% CDF
	L 9: => /DND.  	LKL L	Ls   ;=B9 9Cc                     [         R                  " U5      u  p#U[         R                  :w  a  [        SR	                  U5      5      eUS   U l        US   U l        g)z-Loads client secrets from the given filename.z+The flow specified in {0} is not supported.r:   r;   N)r   loadfileTYPE_WEBrC   r   r:   r;   )r%   filenameclient_typeclient_infos       r   rA   UserOAuth2._load_client_secrets;  s\    #0#9#9(#C -000=DD!" " %[1(9r   c                 j   [         R                  " [        R                  " S5      5      R	                  5       nU[
        [        '   [        R                  " UUS.5      nU R                  R                  5       nUR                  U5        UR                  S/ 5      n[        U R                  5      R                  [        U5      5      n[         R"                  " SU R$                  U R&                  UU[)        SSS9S.UD6n[*        R-                  U5      n	[.        R                  " U5      [
        U	'   U$ )	zCreates a Web Server Flowi   )r   
return_urlr5   zoauth2.callbackT)	_external)r:   r;   scopestateredirect_uri )hashlibsha256osurandom	hexdigestr
   	_CSRF_KEYjsondumpsr.   copyupdater   setr5   unionr   OAuth2WebServerFlowr:   r;   r   r   r   r   )
r%   rM   r'   r   rP   kwextra_scopesr5   flowflow_keys
             r   
_make_flowUserOAuth2._make_flowF  s    ^^BJJt$45??A
'	

$$
 
 ""$
		&vvh+T[[!''L(9:)) nn,, !2dC  ##J/"LL.r   c                     [        S[        5      nUR                  SSU R                  5        UR                  SSU R                  5        U$ )Noauth2z/oauth2authorize	authorizez/oauth2callbackcallback)r   __name__add_url_ruleauthorize_viewcallback_view)r%   bps     r   r8   UserOAuth2._create_blueprinte  sA    x*
*K9L9LM
):t7I7IJ	r   c                 <   [         R                  R                  5       n[         R                  R                  S5      US'   UR	                  SS5      nUc  [         R
                  =(       d    SnU R                  " SSU0UD6nUR                  5       n[        U5      $ )zlFlask view that starts the authorization flow.

Starts flow by redirecting the user to the OAuth2 provider.
r5   rM   N/rR   )	r	   r&   to_dictgetlistr   referrerrd   step1_get_authorize_urlr   )r%   r&   rM   rb   auth_urls        r   rl   UserOAuth2.authorize_viewl  s    
 ||##% !--h7XXXlD1
 ))0SJ=*==//1!!r   c                    S[         R                  ;   a_  [         R                  R                  S[         R                  R                  SS5      5      nSR                  U5      [        R
                  4$  [         R                  S   n[        [           n[         R                  S   n [        R                  " U5      nUS   nUS	   nXc:w  a  S
[        R
                  4$ [        U5      nUc  S
[        R
                  4$  UR                  U5      n	U R&                  R)                  U	5        U R*                  (       a  U R+                  U	5        [-        U5      $ ! [         a    S[        R
                  4s $ f = f! [        [        4 a    S
[        R
                  4s $ f = f! [        R                   aL  n
[         R"                  R%                  U
5        SR                  U
5      nU[        R
                  4s Sn
A
$ Sn
A
ff = f)zFlask view that handles the user's return from OAuth2 provider.

On return, exchanges the authorization code for credentials and stores
the credentials.
errorerror_description zAuthorization failed: {0}rP   codezInvalid requestr   rM   zInvalid request stateNzAn error occurred: {0})r	   r&   r3   r   httplibBAD_REQUESTr
   rX   rB   rY   r   rC   r   step2_exchanger   FlowExchangeErrorr   logger	exceptionr1   putr-   r   )r%   reasonencoded_stateserver_csrfr|   rP   client_csrfrM   rb   credentialsexchange_errorcontents               r   rm   UserOAuth2.callback_view  s    gll"\\%%#W\\%5%5gr%BDF/66v>'') )	:#LL1M!),K<<'D	@JJ}-E-K|,J %*G,?,???";/<*G,?,???	0--d3K 	%""##K0
##A  	:$g&9&999	: H% 	@*G,?,???	@ '' 	0((8.55nEGG////	0sC   53E ) E< F$ E98E9<"F! F!$H8AG?9H?Hc                     [         R                  n[        U[        5      (       d  U R                  R                  5       Ul        UR                  $ )z<The credentials for the current user or None if unavailable.)r   tophasattrr0   r1   r3   r   )r%   ctxs     r   r   UserOAuth2.credentials  s>       s,--,0LL,<,<,>C),,,r   c                     U R                   (       d  gU R                   R                  (       a  U R                   R                  (       d  gg)zAReturns True if there are valid credentials for the current user.FT)r   access_token_expiredrefresh_tokenr%   s    r   has_credentialsUserOAuth2.has_credentials  s2    33$$22r   c                     U R                   (       d  g U R                   R                  S   $ ! [         aE    [        R                  R                  SR                  U R                   R                  5      5         gf = f)a
  Returns the user's email address or None if there are no credentials.

The email address is provided by the current credentials' id_token.
This should not be used as unique identifier as the user can change
their email. If you need a unique identifier, use user_id.
Nr   Invalid id_token {0}r   id_tokenrB   r   r   ry   r   r   s    r   r   UserOAuth2.email  sm     	J##,,W55 	J$$&--d.>.>.G.GHJ	J   - AA<;A<c                     U R                   (       d  g U R                   R                  S   $ ! [         aE    [        R                  R                  SR                  U R                   R                  5      5         gf = f)zReturns the a unique identifier for the user

Returns None if there are no credentials.

The id is provided by the current credentials' id_token.
Nsubr   r   r   s    r   user_idUserOAuth2.user_id  sm     	J##,,U33 	J$$&--d.>.>.G.GHJ	Jr   c                     [        SSU0UD6$ )a  Creates a URL that can be used to start the authorization flow.

When the user is directed to the URL, the authorization flow will
begin. Once complete, the user will be redirected to the specified
return URL.

Any kwargs are passed into the flow constructor.
rM   )zoauth2.authorizer   )r%   rM   r'   s      r   authorize_urlUserOAuth2.authorize_url  s     KjKFKKr   c                 :   ^ ^^ UUU 4S jnU(       a  U" U5      $ U$ )zDecorator to require OAuth2 credentials for a view.

If credentials are not available for the current user, then they will
be redirected to the authorization flow. Once complete, the user will
be redirected back to the original page.
c                 8   >^  [        T 5      UUUU 4S j5       nU$ )Nc                    > TR                  S[        R                  5      n[        TR                  5      nTb  U[        T5      -  nTR                  5       (       a  UTR                  R                  -  n[        U5      nTR                  5       (       a(  TR                  R                  U5      (       a  T" U 0 UD6$ TR                  " U4SU0TD6n[        U5      $ )NrM   r5   )r   r	   urlr]   r5   r   r   list
has_scopesr   r   )	r&   r'   rM   requested_scopesrv   decorator_kwargsr5   r%   wrapped_functions	        r   required_wrapperDUserOAuth2.required.<locals>.curry_wrapper.<locals>.required_wrapper  s    -11,L
#&t{{#3 %$F3$''))$(8(8(?(??$#'(8#9  ((**((334DEE+T<V<<  $11" ,/ , + ,H
 $H--r   r   )r   r   r   r5   r%   s   ` r   curry_wrapper*UserOAuth2.required.<locals>.curry_wrapper  s#    #$. %.2 $#r   rR   )r%   decorated_functionr5   r   r   s   ` `` r   requiredUserOAuth2.required  s    	$:  !344  r   c                     U R                   (       d  [        S5      eU R                   R                  [        R                  " U0 UD65      $ )an  Returns an authorized http instance.

Can only be called if there are valid credentials for the user, such
as inside of a view that is decorated with @required.

Args:
    *args: Positional arguments passed to httplib2.Http constructor.
    **kwargs: Positional arguments passed to httplib2.Http constructor.

Raises:
    ValueError if no credentials are available.
zNo credentials available.)r   rC   rh   httplib2Http)r%   r&   r'   s      r   httpUserOAuth2.http  s>     899))(--*H*HIIr   )r#   r-   r:   r;   r.   r5   r1   r"   )NNNNNN)NN)rj   
__module____qualname____firstlineno____doc__r(   r$   r6   rA   rd   r8   rl   rm   propertyr   r   r   r   r   r   r   __static_attributes__rR   r   r   r   r      s    (0
 >BHL'9R$LL	:>"(0$d - -	 J J J J	L)!VJr   r   )"r   	functoolsr   rS   rY   rU   r   flaskr   r   r   r   r	   r
   r   ImportErrorr   six.moves.http_clientmoveshttp_clientr}   oauth2clientr   r   oauth2client.contribr   
__author__r4   r0   r   rX   r   objectr   rR   r   r   <module>r      s   Tl    	 	I$!  ' '  & 3 7
. $	&		)ZJ ZJ?  I
G
HHIs   *A< <B