service_application.py 6.8 KB

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