web_application.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # -*- coding: utf-8 -*-
  2. """
  3. oauthlib.oauth2.rfc6749
  4. ~~~~~~~~~~~~~~~~~~~~~~~
  5. This module is an implementation of various logic needed
  6. for consuming and providing OAuth 2.0 RFC6749.
  7. """
  8. from __future__ import absolute_import, unicode_literals
  9. from .base import Client
  10. from ..parameters import prepare_grant_uri, prepare_token_request
  11. from ..parameters import parse_authorization_code_response
  12. from ..parameters import parse_token_response
  13. class WebApplicationClient(Client):
  14. """A client utilizing the authorization code grant workflow.
  15. A web application is a confidential client running on a web
  16. server. Resource owners access the client via an HTML user
  17. interface rendered in a user-agent on the device used by the
  18. resource owner. The client credentials as well as any access
  19. token issued to the client are stored on the web server and are
  20. not exposed to or accessible by the resource owner.
  21. The authorization code grant type is used to obtain both access
  22. tokens and refresh tokens and is optimized for confidential clients.
  23. As a redirection-based flow, the client must be capable of
  24. interacting with the resource owner's user-agent (typically a web
  25. browser) and capable of receiving incoming requests (via redirection)
  26. from the authorization server.
  27. """
  28. def __init__(self, client_id, code=None, **kwargs):
  29. super(WebApplicationClient, self).__init__(client_id, **kwargs)
  30. self.code = code
  31. def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
  32. state=None, **kwargs):
  33. """Prepare the authorization code request URI
  34. The client constructs the request URI by adding the following
  35. parameters to the query component of the authorization endpoint URI
  36. using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
  37. :param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
  38. and it should have been registerd with the OAuth
  39. provider prior to use. As described in `Section 3.1.2`_.
  40. :param scope: OPTIONAL. The scope of the access request as described by
  41. Section 3.3`_. These may be any string but are commonly
  42. URIs or various categories such as ``videos`` or ``documents``.
  43. :param state: RECOMMENDED. An opaque value used by the client to maintain
  44. state between the request and callback. The authorization
  45. server includes this value when redirecting the user-agent back
  46. to the client. The parameter SHOULD be used for preventing
  47. cross-site request forgery as described in `Section 10.12`_.
  48. :param kwargs: Extra arguments to include in the request URI.
  49. In addition to supplied parameters, OAuthLib will append the ``client_id``
  50. that was provided in the constructor as well as the mandatory ``response_type``
  51. argument, set to ``code``::
  52. >>> from oauthlib.oauth2 import WebApplicationClient
  53. >>> client = WebApplicationClient('your_id')
  54. >>> client.prepare_request_uri('https://example.com')
  55. 'https://example.com?client_id=your_id&response_type=code'
  56. >>> client.prepare_request_uri('https://example.com', redirect_uri='https://a.b/callback')
  57. 'https://example.com?client_id=your_id&response_type=code&redirect_uri=https%3A%2F%2Fa.b%2Fcallback'
  58. >>> client.prepare_request_uri('https://example.com', scope=['profile', 'pictures'])
  59. 'https://example.com?client_id=your_id&response_type=code&scope=profile+pictures'
  60. >>> client.prepare_request_uri('https://example.com', foo='bar')
  61. 'https://example.com?client_id=your_id&response_type=code&foo=bar'
  62. .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
  63. .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
  64. .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
  65. .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
  66. .. _`Section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
  67. """
  68. return prepare_grant_uri(uri, self.client_id, 'code',
  69. redirect_uri=redirect_uri, scope=scope, state=state, **kwargs)
  70. def prepare_request_body(self, client_id=None, code=None, body='',
  71. redirect_uri=None, **kwargs):
  72. """Prepare the access token request body.
  73. The client makes a request to the token endpoint by adding the
  74. following parameters using the "application/x-www-form-urlencoded"
  75. format in the HTTP request entity-body:
  76. :param client_id: REQUIRED, if the client is not authenticating with the
  77. authorization server as described in `Section 3.2.1`_.
  78. :param code: REQUIRED. The authorization code received from the
  79. authorization server.
  80. :param redirect_uri: REQUIRED, if the "redirect_uri" parameter was included in the
  81. authorization request as described in `Section 4.1.1`_, and their
  82. values MUST be identical.
  83. :param kwargs: Extra parameters to include in the token request.
  84. In addition OAuthLib will add the ``grant_type`` parameter set to
  85. ``authorization_code``.
  86. If the client type is confidential or the client was issued client
  87. credentials (or assigned other authentication requirements), the
  88. client MUST authenticate with the authorization server as described
  89. in `Section 3.2.1`_::
  90. >>> from oauthlib.oauth2 import WebApplicationClient
  91. >>> client = WebApplicationClient('your_id')
  92. >>> client.prepare_request_body(code='sh35ksdf09sf')
  93. 'grant_type=authorization_code&code=sh35ksdf09sf'
  94. >>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar')
  95. 'grant_type=authorization_code&code=sh35ksdf09sf&foo=bar'
  96. .. _`Section 4.1.1`: http://tools.ietf.org/html/rfc6749#section-4.1.1
  97. .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
  98. """
  99. code = code or self.code
  100. return prepare_token_request('authorization_code', code=code, body=body,
  101. client_id=self.client_id, redirect_uri=redirect_uri, **kwargs)
  102. def parse_request_uri_response(self, uri, state=None):
  103. """Parse the URI query for code and state.
  104. If the resource owner grants the access request, the authorization
  105. server issues an authorization code and delivers it to the client by
  106. adding the following parameters to the query component of the
  107. redirection URI using the "application/x-www-form-urlencoded" format:
  108. :param uri: The callback URI that resulted from the user being redirected
  109. back from the provider to you, the client.
  110. :param state: The state provided in the authorization request.
  111. **code**
  112. The authorization code generated by the authorization server.
  113. The authorization code MUST expire shortly after it is issued
  114. to mitigate the risk of leaks. A maximum authorization code
  115. lifetime of 10 minutes is RECOMMENDED. The client MUST NOT
  116. use the authorization code more than once. If an authorization
  117. code is used more than once, the authorization server MUST deny
  118. the request and SHOULD revoke (when possible) all tokens
  119. previously issued based on that authorization code.
  120. The authorization code is bound to the client identifier and
  121. redirection URI.
  122. **state**
  123. If the "state" parameter was present in the authorization request.
  124. This method is mainly intended to enforce strict state checking with
  125. the added benefit of easily extracting parameters from the URI::
  126. >>> from oauthlib.oauth2 import WebApplicationClient
  127. >>> client = WebApplicationClient('your_id')
  128. >>> uri = 'https://example.com/callback?code=sdfkjh345&state=sfetw45'
  129. >>> client.parse_request_uri_response(uri, state='sfetw45')
  130. {'state': 'sfetw45', 'code': 'sdfkjh345'}
  131. >>> client.parse_request_uri_response(uri, state='other')
  132. Traceback (most recent call last):
  133. File "<stdin>", line 1, in <module>
  134. File "oauthlib/oauth2/rfc6749/__init__.py", line 357, in parse_request_uri_response
  135. back from the provider to you, the client.
  136. File "oauthlib/oauth2/rfc6749/parameters.py", line 153, in parse_authorization_code_response
  137. raise MismatchingStateError()
  138. oauthlib.oauth2.rfc6749.errors.MismatchingStateError
  139. """
  140. response = parse_authorization_code_response(uri, state=state)
  141. self._populate_attributes(response)
  142. return response