service_application.py 7.6 KB

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