def validate_scopes(self, request): request.scopes = utils.scope_to_list(request.scope) or utils.scope_to_list( self.request_validator.get_default_scopes(request.client_id, request)) log.debug('Validating access to scopes %r for client %r (%r).', request.scopes, request.client_id, request.client) if not self.request_validator.validate_scopes(request.client_id, request.scopes, request.client, request): raise errors.InvalidScopeError(state=request.state)
def validate_token_parameters(params, scope=None): """Ensures token precence, token type, expiration and scope in params.""" if not 'access_token' in params: raise KeyError("Missing access token parameter.") if not 'token_type' in params: raise KeyError("Missing token type parameter.") # If the issued access token scope is different from the one requested by # the client, the authorization server MUST include the "scope" response # parameter to inform the client of the actual scope granted. # http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.3 new_scope = params.get('scope', None) scope = scope_to_list(scope) if scope and new_scope and set(scope) != set(new_scope): raise Warning("Scope has changed to %s." % new_scope)
def parse_implicit_response(uri, state=None, scope=None): """Parse the implicit token response URI into a dict. If the resource owner grants the access request, the authorization server issues an access token and delivers it to the client by adding the following parameters to the fragment component of the redirection URI using the "application/x-www-form-urlencoded" format: access_token REQUIRED. The access token issued by the authorization server. token_type REQUIRED. The type of the token issued as described in Section 7.1. Value is case insensitive. expires_in RECOMMENDED. The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated. If omitted, the authorization server SHOULD provide the expiration time via other means or document the default value. scope OPTIONAL, if identical to the scope requested by the client, otherwise REQUIRED. The scope of the access token as described by Section 3.3. state REQUIRED if the "state" parameter was present in the client authorization request. The exact value received from the client. HTTP/1.1 302 Found Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA &state=xyz&token_type=example&expires_in=3600 """ fragment = urlparse.urlparse(uri).fragment params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True)) if 'scope' in params: params['scope'] = scope_to_list(params['scope']) if state and params.get('state', None) != state: raise ValueError("Mismatching or missing state in params.") validate_token_parameters(params, scope) return params
def validate_authorization_request(self, request): """Check the authorization request for normal and fatal errors. A normal error could be a missing response_type parameter or the client attempting to access scope it is not allowed to ask authorization for. Normal errors can safely be included in the redirection URI and sent back to the client. Fatal errors occur when the client_id or redirect_uri is invalid or missing. These must be caught by the provider and handled, how this is done is outside of the scope of OAuthLib but showing an error page describing the issue is a good idea. """ # First check for fatal errors # If the request fails due to a missing, invalid, or mismatching # redirection URI, or if the client identifier is missing or invalid, # the authorization server SHOULD inform the resource owner of the # error and MUST NOT automatically redirect the user-agent to the # invalid redirection URI. # REQUIRED. The client identifier as described in Section 2.2. # http://tools.ietf.org/html/rfc6749#section-2.2 if not request.client_id: raise errors.MissingClientIdError(state=request.state) if not self.request_validator.validate_client_id(request.client_id): raise errors.InvalidClientIdError(state=request.state) # OPTIONAL. As described in Section 3.1.2. # http://tools.ietf.org/html/rfc6749#section-3.1.2 if request.redirect_uri is not None: if not is_absolute_uri(request.redirect_uri): raise errors.InvalidRedirectURIError(state=request.state) if not self.request_validator.validate_redirect_uri( request.client_id, request.redirect_uri): raise errors.MismatchingRedirectURIError(state=request.state) else: request.redirect_uri = self.request_validator.get_default_redirect_uri(request.client_id) if not request.redirect_uri: raise errors.MissingRedirectURIError(state=request.state) # Then check for normal errors. # If the resource owner denies the access request or if the request # fails for reasons other than a missing or invalid redirection URI, # the authorization server informs the client by adding the following # parameters to the query component of the redirection URI using the # "application/x-www-form-urlencoded" format, per Appendix B. # http://tools.ietf.org/html/rfc6749#appendix-B # Note that the correct parameters to be added are automatically # populated through the use of specific exceptions. if request.response_type is None: raise errors.InvalidRequestError(state=request.state, description='Missing response_type parameter.') # REQUIRED. Value MUST be set to "code". if request.response_type != 'code': raise errors.UnsupportedResponseTypeError(state=request.state) # OPTIONAL. The scope of the access request as described by Section 3.3 # http://tools.ietf.org/html/rfc6749#section-3.3 request.scopes = utils.scope_to_list(request.scope) or self.request_validator.get_default_scopes(request.client_id) if not self.request_validator.validate_scopes(request.client_id, request.scopes, request.client): raise errors.InvalidScopeError(state=request.state) return True, request.scopes, { 'client_id': request.client_id, 'redirect_uri': request.redirect_uri, 'response_type': request.response_type }
def parse_token_response(body, scope=None): """Parse the JSON token response body into a dict. The authorization server issues an access token and optional refresh token, and constructs the response by adding the following parameters to the entity body of the HTTP response with a 200 (OK) status code: access_token REQUIRED. The access token issued by the authorization server. token_type REQUIRED. The type of the token issued as described in `Section 7.1`_. Value is case insensitive. expires_in RECOMMENDED. The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated. If omitted, the authorization server SHOULD provide the expiration time via other means or document the default value. refresh_token OPTIONAL. The refresh token which can be used to obtain new access tokens using the same authorization grant as described in `Section 6`_. scope OPTIONAL, if identical to the scope requested by the client, otherwise REQUIRED. The scope of the access token as described by `Section 3.3`_. The parameters are included in the entity body of the HTTP response using the "application/json" media type as defined by [`RFC4627`_]. The parameters are serialized into a JSON structure by adding each parameter at the highest structure level. Parameter names and string values are included as JSON strings. Numerical values are included as JSON numbers. The order of parameters does not matter and can vary. For example: HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" } .. _`Section 7.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-7.1 .. _`Section 6`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-6 .. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3 .. _`RFC4627`: http://tools.ietf.org/html/rfc4627 """ params = json.loads(body) if 'scope' in params: params['scope'] = scope_to_list(params['scope']) validate_token_parameters(params, scope) return params
def validate_authorization_request(self, request): """Check the authorization request for normal and fatal errors. A normal error could be a missing response_type parameter or the client attempting to access scope it is not allowed to ask authorization for. Normal errors can safely be included in the redirection URI and sent back to the client. Fatal errors occur when the client_id or redirect_uri is invalid or missing. These must be caught by the provider and handled, how this is done is outside of the scope of OAuthLib but showing an error page describing the issue is a good idea. """ # First check for fatal errors # If the request fails due to a missing, invalid, or mismatching # redirection URI, or if the client identifier is missing or invalid, # the authorization server SHOULD inform the resource owner of the # error and MUST NOT automatically redirect the user-agent to the # invalid redirection URI. # REQUIRED. The client identifier as described in Section 2.2. # http://tools.ietf.org/html/rfc6749#section-2.2 if not request.client_id: raise errors.MissingClientIdError(state=request.state) if not self.request_validator.validate_client_id(request.client_id): raise errors.InvalidClientIdError(state=request.state) # OPTIONAL. As described in Section 3.1.2. # http://tools.ietf.org/html/rfc6749#section-3.1.2 if request.redirect_uri is not None: if not is_absolute_uri(request.redirect_uri): raise errors.InvalidRedirectURIError(state=request.state) if not self.request_validator.validate_redirect_uri( request.client_id, request.redirect_uri): raise errors.MismatchingRedirectURIError(state=request.state) else: request.redirect_uri = self.request_validator.get_default_redirect_uri( request.client_id) if not request.redirect_uri: raise errors.MissingRedirectURIError(state=request.state) # Then check for normal errors. # If the resource owner denies the access request or if the request # fails for reasons other than a missing or invalid redirection URI, # the authorization server informs the client by adding the following # parameters to the query component of the redirection URI using the # "application/x-www-form-urlencoded" format, per Appendix B. # http://tools.ietf.org/html/rfc6749#appendix-B # Note that the correct parameters to be added are automatically # populated through the use of specific exceptions. if request.response_type is None: raise errors.InvalidRequestError( state=request.state, description='Missing response_type parameter.') # REQUIRED. Value MUST be set to "code". if request.response_type != 'code': raise errors.UnsupportedResponseTypeError(state=request.state) # OPTIONAL. The scope of the access request as described by Section 3.3 # http://tools.ietf.org/html/rfc6749#section-3.3 request.scopes = utils.scope_to_list( request.scope) or self.request_validator.get_default_scopes( request.client_id) if not self.request_validator.validate_scopes( request.client_id, request.scopes, request.client): raise errors.InvalidScopeError(state=request.state) return True, request.scopes, { 'client_id': request.client_id, 'redirect_uri': request.redirect_uri, 'response_type': request.response_type }