1# -*- coding: utf-8 -*- 
    2""" 
    3oauthlib.oauth2.rfc6749 
    4~~~~~~~~~~~~~~~~~~~~~~~ 
    5 
    6This module is an implementation of various logic needed 
    7for consuming and providing OAuth 2.0 RFC6749. 
    8""" 
    9import time 
    10 
    11from oauthlib.common import to_unicode 
    12 
    13from ..parameters import prepare_token_request 
    14from .base import Client 
    15 
    16 
    17class ServiceApplicationClient(Client): 
    18    """A public client utilizing the JWT bearer grant. 
    19 
    20    JWT bearer tokes can be used to request an access token when a client 
    21    wishes to utilize an existing trust relationship, expressed through the 
    22    semantics of (and digital signature or keyed message digest calculated 
    23    over) the JWT, without a direct user approval step at the authorization 
    24    server. 
    25 
    26    This grant type does not involve an authorization step. It may be 
    27    used by both public and confidential clients. 
    28    """ 
    29 
    30    grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer' 
    31 
    32    def __init__(self, client_id, private_key=None, subject=None, issuer=None, 
    33                 audience=None, **kwargs): 
    34        """Initialize a JWT client with defaults for implicit use later. 
    35 
    36        :param client_id: Client identifier given by the OAuth provider upon 
    37                          registration. 
    38 
    39        :param private_key: Private key used for signing and encrypting. 
    40                            Must be given as a string. 
    41 
    42        :param subject: The principal that is the subject of the JWT, i.e. 
    43                        which user is the token requested on behalf of. 
    44                        For example, ``foo@example.com. 
    45 
    46        :param issuer: The JWT MUST contain an "iss" (issuer) claim that 
    47                       contains a unique identifier for the entity that issued 
    48                       the JWT. For example, ``your-client@provider.com``. 
    49 
    50        :param audience: A value identifying the authorization server as an 
    51                         intended audience, e.g. 
    52                         ``https://provider.com/oauth2/token``. 
    53 
    54        :param kwargs: Additional arguments to pass to base client, such as 
    55                       state and token. See ``Client.__init__.__doc__`` for 
    56                       details. 
    57        """ 
    58        super().__init__(client_id, **kwargs) 
    59        self.private_key = private_key 
    60        self.subject = subject 
    61        self.issuer = issuer 
    62        self.audience = audience 
    63 
    64    def prepare_request_body(self, 
    65                             private_key=None, 
    66                             subject=None, 
    67                             issuer=None, 
    68                             audience=None, 
    69                             expires_at=None, 
    70                             issued_at=None, 
    71                             extra_claims=None, 
    72                             body='', 
    73                             scope=None, 
    74                             include_client_id=False, 
    75                             **kwargs): 
    76        """Create and add a JWT assertion to the request body. 
    77 
    78        :param private_key: Private key used for signing and encrypting. 
    79                            Must be given as a string. 
    80 
    81        :param subject: (sub) The principal that is the subject of the JWT, 
    82                        i.e.  which user is the token requested on behalf of. 
    83                        For example, ``foo@example.com. 
    84 
    85        :param issuer: (iss) The JWT MUST contain an "iss" (issuer) claim that 
    86                       contains a unique identifier for the entity that issued 
    87                       the JWT. For example, ``your-client@provider.com``. 
    88 
    89        :param audience: (aud) A value identifying the authorization server as an 
    90                         intended audience, e.g. 
    91                         ``https://provider.com/oauth2/token``. 
    92 
    93        :param expires_at: A unix expiration timestamp for the JWT. Defaults 
    94                           to an hour from now, i.e. ``round(time.time()) + 3600``. 
    95 
    96        :param issued_at: A unix timestamp of when the JWT was created. 
    97                          Defaults to now, i.e. ``time.time()``. 
    98 
    99        :param extra_claims: A dict of additional claims to include in the JWT. 
    100 
    101        :param body: Existing request body (URL encoded string) to embed parameters 
    102                     into. This may contain extra parameters. Default ''. 
    103 
    104        :param scope: The scope of the access request. 
    105 
    106        :param include_client_id: `True` to send the `client_id` in the 
    107                                  body of the upstream request. This is required 
    108                                  if the client is not authenticating with the 
    109                                  authorization server as described in 
    110                                  `Section 3.2.1`_. False otherwise (default). 
    111        :type include_client_id: Boolean 
    112 
    113        :param not_before: A unix timestamp after which the JWT may be used. 
    114                           Not included unless provided. * 
    115 
    116        :param jwt_id: A unique JWT token identifier. Not included unless 
    117                       provided. * 
    118 
    119        :param kwargs: Extra credentials to include in the token request. 
    120 
    121        Parameters marked with a `*` above are not explicit arguments in the 
    122        function signature, but are specially documented arguments for items 
    123        appearing in the generic `**kwargs` keyworded input. 
    124 
    125        The "scope" parameter may be used, as defined in the Assertion 
    126        Framework for OAuth 2.0 Client Authentication and Authorization Grants 
    127        [I-D.ietf-oauth-assertions] specification, to indicate the requested 
    128        scope. 
    129 
    130        Authentication of the client is optional, as described in 
    131        `Section 3.2.1`_ of OAuth 2.0 [RFC6749] and consequently, the 
    132        "client_id" is only needed when a form of client authentication that 
    133        relies on the parameter is used. 
    134 
    135        The following non-normative example demonstrates an Access Token 
    136        Request with a JWT as an authorization grant (with extra line breaks 
    137        for display purposes only): 
    138 
    139        .. code-block: http 
    140 
    141            POST /token.oauth2 HTTP/1.1 
    142            Host: as.example.com 
    143            Content-Type: application/x-www-form-urlencoded 
    144 
    145            grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer 
    146            &assertion=eyJhbGciOiJFUzI1NiJ9. 
    147            eyJpc3Mi[...omitted for brevity...]. 
    148            J9l-ZhwP[...omitted for brevity...] 
    149 
    150        .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1 
    151        """ 
    152        import jwt  # noqa: PLC0415 
    153 
    154        key = private_key or self.private_key 
    155        if not key: 
    156            raise ValueError('An encryption key must be supplied to make JWT' 
    157                             ' token requests.') 
    158        claim = { 
    159            'iss': issuer or self.issuer, 
    160            'aud': audience or self.audience, 
    161            'sub': subject or self.subject, 
    162            'exp': int(expires_at or time.time() + 3600), 
    163            'iat': int(issued_at or time.time()), 
    164        } 
    165 
    166        for attr in ('iss', 'aud', 'sub'): 
    167            if claim[attr] is None: 
    168                raise ValueError( 
    169                        'Claim must include %s but none was given.' % attr) 
    170 
    171        if 'not_before' in kwargs: 
    172            claim['nbf'] = kwargs.pop('not_before') 
    173 
    174        if 'jwt_id' in kwargs: 
    175            claim['jti'] = kwargs.pop('jwt_id') 
    176 
    177        claim.update(extra_claims or {}) 
    178 
    179        assertion = jwt.encode(claim, key, 'RS256') 
    180        assertion = to_unicode(assertion) 
    181 
    182        kwargs['client_id'] = self.client_id 
    183        kwargs['include_client_id'] = include_client_id 
    184        scope = self.scope if scope is None else scope 
    185        return prepare_token_request(self.grant_type, 
    186                                     body=body, 
    187                                     assertion=assertion, 
    188                                     scope=scope, 
    189                                     **kwargs)