1""" 
    2oauthlib.openid.connect.core.endpoints.userinfo 
    3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    4 
    5This module is an implementation of userinfo endpoint. 
    6""" 
    7import json 
    8import logging 
    9 
    10from oauthlib.common import Request 
    11from oauthlib.oauth2.rfc6749 import errors 
    12from oauthlib.oauth2.rfc6749.endpoints.base import ( 
    13    BaseEndpoint, catch_errors_and_unavailability, 
    14) 
    15from oauthlib.oauth2.rfc6749.tokens import BearerToken 
    16 
    17log = logging.getLogger(__name__) 
    18 
    19 
    20class UserInfoEndpoint(BaseEndpoint): 
    21    """Authorizes access to userinfo resource. 
    22    """ 
    23    def __init__(self, request_validator): 
    24        self.bearer = BearerToken(request_validator, None, None, None) 
    25        self.request_validator = request_validator 
    26        BaseEndpoint.__init__(self) 
    27 
    28    @catch_errors_and_unavailability 
    29    def create_userinfo_response(self, uri, http_method='GET', body=None, headers=None): 
    30        """Validate BearerToken and return userinfo from RequestValidator 
    31 
    32        The UserInfo Endpoint MUST return a 
    33        content-type header to indicate which format is being returned. The 
    34        content-type of the HTTP response MUST be application/json if the 
    35        response body is a text JSON object; the response body SHOULD be encoded 
    36        using UTF-8. 
    37        """ 
    38        request = Request(uri, http_method, body, headers) 
    39        request.scopes = ["openid"] 
    40        self.validate_userinfo_request(request) 
    41 
    42        claims = self.request_validator.get_userinfo_claims(request) 
    43        if claims is None: 
    44            log.error('Userinfo MUST have claims for %r.', request) 
    45            raise errors.ServerError(status_code=500) 
    46 
    47        if isinstance(claims, dict): 
    48            resp_headers = { 
    49                'Content-Type': 'application/json' 
    50            } 
    51            if "sub" not in claims: 
    52                log.error('Userinfo MUST have "sub" for %r.', request) 
    53                raise errors.ServerError(status_code=500) 
    54            body = json.dumps(claims) 
    55        elif isinstance(claims, str): 
    56            resp_headers = { 
    57                'Content-Type': 'application/jwt' 
    58            } 
    59            body = claims 
    60        else: 
    61            log.error('Userinfo return unknown response for %r.', request) 
    62            raise errors.ServerError(status_code=500) 
    63        log.debug('Userinfo access valid for %r.', request) 
    64        return resp_headers, body, 200 
    65 
    66    def validate_userinfo_request(self, request): 
    67        """Ensure the request is valid. 
    68 
    69        5.3.1.  UserInfo Request 
    70        The Client sends the UserInfo Request using either HTTP GET or HTTP 
    71        POST. The Access Token obtained from an OpenID Connect Authentication 
    72        Request MUST be sent as a Bearer Token, per `Section 2`_ of OAuth 2.0 
    73        Bearer Token Usage [RFC6750]. 
    74 
    75        It is RECOMMENDED that the request use the HTTP GET method and the 
    76        Access Token be sent using the Authorization header field. 
    77 
    78        The following is a non-normative example of a UserInfo Request: 
    79 
    80        .. code-block:: http 
    81 
    82            GET /userinfo HTTP/1.1 
    83            Host: server.example.com 
    84            Authorization: Bearer SlAV32hkKG 
    85 
    86        5.3.3. UserInfo Error Response 
    87        When an error condition occurs, the UserInfo Endpoint returns an Error 
    88        Response as defined in `Section 3`_ of OAuth 2.0 Bearer Token Usage 
    89        [RFC6750]. (HTTP errors unrelated to RFC 6750 are returned to the User 
    90        Agent using the appropriate HTTP status code.) 
    91 
    92        The following is a non-normative example of a UserInfo Error Response: 
    93 
    94        .. code-block:: http 
    95 
    96            HTTP/1.1 401 Unauthorized 
    97            WWW-Authenticate: Bearer error="invalid_token", 
    98                error_description="The Access Token expired" 
    99 
    100        .. _`Section 2`: https://datatracker.ietf.org/doc/html/rfc6750#section-2 
    101        .. _`Section 3`: https://datatracker.ietf.org/doc/html/rfc6750#section-3 
    102        """ 
    103        if not self.bearer.validate_request(request): 
    104            raise errors.InvalidTokenError() 
    105        if "openid" not in request.scopes: 
    106            raise errors.InsufficientScopeError()