Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/oauthlib/oauth1/rfc5849/endpoints/access_token.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

58 statements  

1# -*- coding: utf-8 -*- 

2""" 

3oauthlib.oauth1.rfc5849.endpoints.access_token 

4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

5 

6This module is an implementation of the access token provider logic of 

7OAuth 1.0 RFC 5849. It validates the correctness of access token requests, 

8creates and persists tokens as well as create the proper response to be 

9returned to the client. 

10""" 

11import logging 

12 

13from oauthlib.common import urlencode 

14 

15from .. import errors 

16from .base import BaseEndpoint 

17 

18log = logging.getLogger(__name__) 

19 

20 

21class AccessTokenEndpoint(BaseEndpoint): 

22 

23 """An endpoint responsible for providing OAuth 1 access tokens. 

24 

25 Typical use is to instantiate with a request validator and invoke the 

26 ``create_access_token_response`` from a view function. The tuple returned 

27 has all information necessary (body, status, headers) to quickly form 

28 and return a proper response. See :doc:`/oauth1/validator` for details on which 

29 validator methods to implement for this endpoint. 

30 """ 

31 

32 def create_access_token(self, request, credentials): 

33 """Create and save a new access token. 

34 

35 Similar to OAuth 2, indication of granted scopes will be included as a 

36 space separated list in ``oauth_authorized_realms``. 

37 

38 :param request: OAuthlib request. 

39 :type request: oauthlib.common.Request 

40 :returns: The token as an urlencoded string. 

41 """ 

42 request.realms = self.request_validator.get_realms( 

43 request.resource_owner_key, request) 

44 token = { 

45 'oauth_token': self.token_generator(), 

46 'oauth_token_secret': self.token_generator(), 

47 # Backport the authorized scopes indication used in OAuth2 

48 'oauth_authorized_realms': ' '.join(request.realms) 

49 } 

50 token.update(credentials) 

51 self.request_validator.save_access_token(token, request) 

52 return urlencode(token.items()) 

53 

54 def create_access_token_response(self, uri, http_method='GET', body=None, 

55 headers=None, credentials=None): 

56 """Create an access token response, with a new request token if valid. 

57 

58 :param uri: The full URI of the token request. 

59 :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc. 

60 :param body: The request body as a string. 

61 :param headers: The request headers as a dict. 

62 :param credentials: A list of extra credentials to include in the token. 

63 :returns: A tuple of 3 elements. 

64 1. A dict of headers to set on the response. 

65 2. The response body as a string. 

66 3. The response status code as an integer. 

67 

68 An example of a valid request:: 

69 

70 >>> from your_validator import your_validator 

71 >>> from oauthlib.oauth1 import AccessTokenEndpoint 

72 >>> endpoint = AccessTokenEndpoint(your_validator) 

73 >>> h, b, s = endpoint.create_access_token_response( 

74 ... 'https://your.provider/access_token?foo=bar', 

75 ... headers={ 

76 ... 'Authorization': 'OAuth oauth_token=234lsdkf....' 

77 ... }, 

78 ... credentials={ 

79 ... 'my_specific': 'argument', 

80 ... }) 

81 >>> h 

82 {'Content-Type': 'application/x-www-form-urlencoded'} 

83 >>> b 

84 'oauth_token=lsdkfol23w54jlksdef&oauth_token_secret=qwe089234lkjsdf&oauth_authorized_realms=movies+pics&my_specific=argument' 

85 >>> s 

86 200 

87 

88 An response to invalid request would have a different body and status:: 

89 

90 >>> b 

91 'error=invalid_request&description=missing+resource+owner+key' 

92 >>> s 

93 400 

94 

95 The same goes for an an unauthorized request: 

96 

97 >>> b 

98 '' 

99 >>> s 

100 401 

101 """ 

102 resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'} 

103 try: 

104 request = self._create_request(uri, http_method, body, headers) 

105 valid, processed_request = self.validate_access_token_request( 

106 request) 

107 if valid: 

108 token = self.create_access_token(request, credentials or {}) 

109 self.request_validator.invalidate_request_token( 

110 request.client_key, 

111 request.resource_owner_key, 

112 request) 

113 return resp_headers, token, 200 

114 else: 

115 return {}, None, 401 

116 except errors.OAuth1Error as e: 

117 return resp_headers, e.urlencoded, e.status_code 

118 

119 def validate_access_token_request(self, request): 

120 """Validate an access token request. 

121 

122 :param request: OAuthlib request. 

123 :type request: oauthlib.common.Request 

124 :raises: OAuth1Error if the request is invalid. 

125 :returns: A tuple of 2 elements. 

126 1. The validation result (True or False). 

127 2. The request object. 

128 """ 

129 self._check_transport_security(request) 

130 self._check_mandatory_parameters(request) 

131 

132 if not request.resource_owner_key: 

133 raise errors.InvalidRequestError( 

134 description='Missing resource owner.') 

135 

136 if not self.request_validator.check_request_token( 

137 request.resource_owner_key): 

138 raise errors.InvalidRequestError( 

139 description='Invalid resource owner key format.') 

140 

141 if not request.verifier: 

142 raise errors.InvalidRequestError( 

143 description='Missing verifier.') 

144 

145 if not self.request_validator.check_verifier(request.verifier): 

146 raise errors.InvalidRequestError( 

147 description='Invalid verifier format.') 

148 

149 if not self.request_validator.validate_timestamp_and_nonce( 

150 request.client_key, request.timestamp, request.nonce, request, 

151 request_token=request.resource_owner_key): 

152 return False, request 

153 

154 # The server SHOULD return a 401 (Unauthorized) status code when 

155 # receiving a request with invalid client credentials. 

156 # Note: This is postponed in order to avoid timing attacks, instead 

157 # a dummy client is assigned and used to maintain near constant 

158 # time request verification. 

159 # 

160 # Note that early exit would enable client enumeration 

161 valid_client = self.request_validator.validate_client_key( 

162 request.client_key, request) 

163 if not valid_client: 

164 request.client_key = self.request_validator.dummy_client 

165 

166 # The server SHOULD return a 401 (Unauthorized) status code when 

167 # receiving a request with invalid or expired token. 

168 # Note: This is postponed in order to avoid timing attacks, instead 

169 # a dummy token is assigned and used to maintain near constant 

170 # time request verification. 

171 # 

172 # Note that early exit would enable resource owner enumeration 

173 valid_resource_owner = self.request_validator.validate_request_token( 

174 request.client_key, request.resource_owner_key, request) 

175 if not valid_resource_owner: 

176 request.resource_owner_key = self.request_validator.dummy_request_token 

177 

178 # The server MUST verify (Section 3.2) the validity of the request, 

179 # ensure that the resource owner has authorized the provisioning of 

180 # token credentials to the client, and ensure that the temporary 

181 # credentials have not expired or been used before. The server MUST 

182 # also verify the verification code received from the client. 

183 # .. _`Section 3.2`: https://tools.ietf.org/html/rfc5849#section-3.2 

184 # 

185 # Note that early exit would enable resource owner authorization 

186 # verifier enumertion. 

187 valid_verifier = self.request_validator.validate_verifier( 

188 request.client_key, 

189 request.resource_owner_key, 

190 request.verifier, 

191 request) 

192 

193 valid_signature = self._check_signature(request, is_token_request=True) 

194 

195 # log the results to the validator_log 

196 # this lets us handle internal reporting and analysis 

197 request.validator_log['client'] = valid_client 

198 request.validator_log['resource_owner'] = valid_resource_owner 

199 request.validator_log['verifier'] = valid_verifier 

200 request.validator_log['signature'] = valid_signature 

201 

202 # We delay checking validity until the very end, using dummy values for 

203 # calculations and fetching secrets/keys to ensure the flow of every 

204 # request remains almost identical regardless of whether valid values 

205 # have been supplied. This ensures near constant time execution and 

206 # prevents malicious users from guessing sensitive information 

207 v = all((valid_client, valid_resource_owner, valid_verifier, 

208 valid_signature)) 

209 if not v: 

210 log.info("[Failure] request verification failed.") 

211 log.info("Valid client:, %s", valid_client) 

212 log.info("Valid token:, %s", valid_resource_owner) 

213 log.info("Valid verifier:, %s", valid_verifier) 

214 log.info("Valid signature:, %s", valid_signature) 

215 return v, request