joserfc 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. joserfc-0.1.0/LICENSE +28 -0
  2. joserfc-0.1.0/PKG-INFO +38 -0
  3. joserfc-0.1.0/README.md +3 -0
  4. joserfc-0.1.0/pyproject.toml +43 -0
  5. joserfc-0.1.0/setup.cfg +4 -0
  6. joserfc-0.1.0/src/joserfc/__init__.py +5 -0
  7. joserfc-0.1.0/src/joserfc/errors.py +88 -0
  8. joserfc-0.1.0/src/joserfc/jwe.py +70 -0
  9. joserfc-0.1.0/src/joserfc/jwk.py +86 -0
  10. joserfc-0.1.0/src/joserfc/jws.py +192 -0
  11. joserfc-0.1.0/src/joserfc/jwt.py +79 -0
  12. joserfc-0.1.0/src/joserfc/registry.py +145 -0
  13. joserfc-0.1.0/src/joserfc/rfc7515/__init__.py +0 -0
  14. joserfc-0.1.0/src/joserfc/rfc7515/compact.py +63 -0
  15. joserfc-0.1.0/src/joserfc/rfc7515/json.py +123 -0
  16. joserfc-0.1.0/src/joserfc/rfc7515/model.py +36 -0
  17. joserfc-0.1.0/src/joserfc/rfc7515/registry.py +55 -0
  18. joserfc-0.1.0/src/joserfc/rfc7515/types.py +92 -0
  19. joserfc-0.1.0/src/joserfc/rfc7516/__init__.py +0 -0
  20. joserfc-0.1.0/src/joserfc/rfc7516/compact.py +57 -0
  21. joserfc-0.1.0/src/joserfc/rfc7516/message.py +100 -0
  22. joserfc-0.1.0/src/joserfc/rfc7516/models.py +79 -0
  23. joserfc-0.1.0/src/joserfc/rfc7516/registry.py +97 -0
  24. joserfc-0.1.0/src/joserfc/rfc7516/types.py +63 -0
  25. joserfc-0.1.0/src/joserfc/rfc7517/__init__.py +13 -0
  26. joserfc-0.1.0/src/joserfc/rfc7517/keygen.py +34 -0
  27. joserfc-0.1.0/src/joserfc/rfc7517/keyset.py +56 -0
  28. joserfc-0.1.0/src/joserfc/rfc7517/models.py +258 -0
  29. joserfc-0.1.0/src/joserfc/rfc7517/pem.py +120 -0
  30. joserfc-0.1.0/src/joserfc/rfc7517/types.py +26 -0
  31. joserfc-0.1.0/src/joserfc/rfc7518/__init__.py +0 -0
  32. joserfc-0.1.0/src/joserfc/rfc7518/ec_key.py +135 -0
  33. joserfc-0.1.0/src/joserfc/rfc7518/jwe_algs.py +357 -0
  34. joserfc-0.1.0/src/joserfc/rfc7518/jwe_encs.py +138 -0
  35. joserfc-0.1.0/src/joserfc/rfc7518/jwe_zips.py +22 -0
  36. joserfc-0.1.0/src/joserfc/rfc7518/jws_algs.py +201 -0
  37. joserfc-0.1.0/src/joserfc/rfc7518/oct_key.py +61 -0
  38. joserfc-0.1.0/src/joserfc/rfc7518/rsa_key.py +154 -0
  39. joserfc-0.1.0/src/joserfc/rfc7518/util.py +12 -0
  40. joserfc-0.1.0/src/joserfc/rfc7519/__init__.py +0 -0
  41. joserfc-0.1.0/src/joserfc/rfc7519/claims.py +41 -0
  42. joserfc-0.1.0/src/joserfc/rfc7519/registry.py +22 -0
  43. joserfc-0.1.0/src/joserfc/rfc7519/validators.py +133 -0
  44. joserfc-0.1.0/src/joserfc/rfc7638/__init__.py +16 -0
  45. joserfc-0.1.0/src/joserfc/rfc8037/__init__.py +0 -0
  46. joserfc-0.1.0/src/joserfc/rfc8037/jws_eddsa.py +22 -0
  47. joserfc-0.1.0/src/joserfc/rfc8037/okp_key.py +163 -0
  48. joserfc-0.1.0/src/joserfc/rfc8812/__init__.py +14 -0
  49. joserfc-0.1.0/src/joserfc/util.py +58 -0
  50. joserfc-0.1.0/src/joserfc.egg-info/PKG-INFO +38 -0
  51. joserfc-0.1.0/src/joserfc.egg-info/SOURCES.txt +52 -0
  52. joserfc-0.1.0/src/joserfc.egg-info/dependency_links.txt +1 -0
  53. joserfc-0.1.0/src/joserfc.egg-info/requires.txt +1 -0
  54. joserfc-0.1.0/src/joserfc.egg-info/top_level.txt +1 -0
joserfc-0.1.0/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Hsiaoming Yang
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
joserfc-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.1
2
+ Name: joserfc
3
+ Version: 0.1.0
4
+ Summary: The ultimate Python library for JOSE RFCs, including JWS, JWE, JWK, JWA, JWT
5
+ Author-email: Hsiaoming Yang <me@lepture.com>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2023, Hsiaoming Yang
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright notice, this
14
+ list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+
20
+ 3. Neither the name of the copyright holder nor the names of its
21
+ contributors may be used to endorse or promote products derived from
22
+ this software without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ Project-URL: Documentation, https://jose.authlib.org/
36
+ Project-URL: Blog, https://blog.authlib.org/
37
+ Requires-Python: >=3.8
38
+ License-File: LICENSE
@@ -0,0 +1,3 @@
1
+ # JOSE RFCs
2
+
3
+ The Python implementations of JOSE RFCs.
@@ -0,0 +1,43 @@
1
+ [project]
2
+ name = "joserfc"
3
+ description = "The ultimate Python library for JOSE RFCs, including JWS, JWE, JWK, JWA, JWT"
4
+ authors = [{name = "Hsiaoming Yang", email="me@lepture.com"}]
5
+ dependencies = [
6
+ "cryptography",
7
+ ]
8
+ license = {file = "LICENSE"}
9
+ requires-python = ">=3.8"
10
+ dynamic = ["version"]
11
+
12
+ [project.urls]
13
+ Documentation = "https://jose.authlib.org/"
14
+ Blog = "https://blog.authlib.org/"
15
+
16
+ [build-system]
17
+ requires = ["setuptools"]
18
+ build-backend = "setuptools.build_meta"
19
+
20
+ [tool.setuptools.dynamic]
21
+ version = {attr = "joserfc.__version__"}
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [tool.pytest.ini_options]
27
+ pythonpath = ["src", "."]
28
+ testpaths = ["tests"]
29
+ filterwarnings = ["error"]
30
+
31
+ [tool.coverage.run]
32
+ branch = true
33
+ source = ["joserfc"]
34
+
35
+ [tool.coverage.paths]
36
+ source = ["src"]
37
+
38
+ [tool.coverage.report]
39
+ exclude_lines = [
40
+ "pragma: no cover",
41
+ "raise NotImplementedError",
42
+ "@(abc\\.)?abstractmethod",
43
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+
2
+ __version__ = "0.1.0"
3
+ __homepage__ = "https://jose.authlib.org/"
4
+ __author__ = "Hsiaoming Yang <me@lepture.com>"
5
+ __license__ = "BSD-3-Clause"
@@ -0,0 +1,88 @@
1
+ from typing import Optional
2
+
3
+
4
+ class JoseError(Exception):
5
+ """Base Exception for all errors in joserfc."""
6
+
7
+ #: short-string error code
8
+ error: str = ''
9
+ #: long-string to describe this error
10
+ description: str = ''
11
+
12
+ def __init__(self, description: Optional[str]=None, error: Optional[str]=None):
13
+ if error is not None:
14
+ self.error = error
15
+ if description is not None:
16
+ self.description = description
17
+
18
+ message = '{}: {}'.format(self.error, self.description)
19
+ super(JoseError, self).__init__(message)
20
+
21
+ def __repr__(self):
22
+ return '<{} "{}">'.format(self.__class__.__name__, self.error)
23
+
24
+
25
+ class DecodeError(JoseError):
26
+ error: str = 'decode_error'
27
+
28
+
29
+ class MissingAlgorithmError(JoseError):
30
+ error: str = 'missing_algorithm'
31
+ description: str = 'Missing "alg" value in header'
32
+
33
+
34
+ class MissingEncryptionError(JoseError):
35
+ error: str = 'missing_encryption'
36
+ description: str = 'Missing "enc" value in header'
37
+
38
+
39
+ class InvalidKeyManagementModeError(JoseError):
40
+ error: str = 'invalid_key_management_mode'
41
+ description: str = ''
42
+
43
+
44
+ class BadSignatureError(JoseError):
45
+ error: str = 'bad_signature'
46
+
47
+
48
+ class InvalidClaimError(JoseError):
49
+ error: str = 'invalid_claim'
50
+
51
+ def __init__(self, claim):
52
+ description = f'Invalid claim: "{claim}"'
53
+ super(InvalidClaimError, self).__init__(description=description)
54
+
55
+
56
+ class MissingClaimError(JoseError):
57
+ error: str = 'missing_claim'
58
+
59
+ def __init__(self, claim):
60
+ description = f'Missing claim: "{claim}"'
61
+ super(MissingClaimError, self).__init__(description=description)
62
+
63
+
64
+ class InsecureClaimError(JoseError):
65
+ error: str = 'insecure_claim'
66
+
67
+ def __init__(self, claim):
68
+ description = f'Insecure claim "{claim}"'
69
+ super(InsecureClaimError, self).__init__(description=description)
70
+
71
+
72
+ class ExpiredTokenError(JoseError):
73
+ error: str = 'expired_token'
74
+ description: str = 'The token is expired'
75
+
76
+
77
+ class InvalidTokenError(JoseError):
78
+ error: str = 'invalid_token'
79
+ description: str = 'The token is not valid yet'
80
+
81
+
82
+ class InvalidTypeError(JoseError):
83
+ error: str = 'invalid_type'
84
+ description: str = 'The "typ" value in header is invalid'
85
+
86
+
87
+ class InvalidPayloadError(JoseError):
88
+ error: str = 'invalid_payload'
@@ -0,0 +1,70 @@
1
+ from typing import Optional, AnyStr
2
+ from .rfc7516 import types
3
+ from .rfc7516.types import Header, EncryptionData, Recipient
4
+ from .rfc7516.registry import (
5
+ JWERegistry,
6
+ default_registry,
7
+ )
8
+ from .rfc7516.message import perform_encrypt, perform_decrypt
9
+ from .rfc7516.compact import represent_compact, extract_compact
10
+ from .rfc7518.jwe_algs import JWE_ALG_MODELS
11
+ from .rfc7518.jwe_encs import JWE_ENC_MODELS
12
+ from .rfc7518.jwe_zips import JWE_ZIP_MODELS
13
+ from .jwk import KeyFlexible, guess_key
14
+ from .util import to_bytes
15
+
16
+
17
+ __all__ = [
18
+ 'types',
19
+ 'JWERegistry',
20
+ 'encrypt_compact',
21
+ 'decrypt_compact',
22
+ 'extract_compact',
23
+ ]
24
+
25
+ def __register():
26
+ for _alg in JWE_ALG_MODELS:
27
+ JWERegistry.register(_alg)
28
+
29
+ for _enc in JWE_ENC_MODELS:
30
+ JWERegistry.register(_enc)
31
+
32
+ for _zip in JWE_ZIP_MODELS:
33
+ JWERegistry.register(_zip)
34
+
35
+ __register()
36
+
37
+
38
+ def encrypt_compact(
39
+ protected: Header,
40
+ payload: bytes,
41
+ public_key: KeyFlexible,
42
+ registry: Optional[JWERegistry]=None) -> bytes:
43
+
44
+ if registry is None:
45
+ registry = default_registry
46
+
47
+ obj = EncryptionData(protected, payload)
48
+ recipient = Recipient(obj)
49
+ key = guess_key(public_key, recipient)
50
+ recipient.recipient_key = key
51
+ obj.recipients.append(recipient)
52
+ obj.compact = True
53
+ perform_encrypt(obj, registry)
54
+ return represent_compact(obj)
55
+
56
+
57
+ def decrypt_compact(
58
+ value: AnyStr,
59
+ private_key: KeyFlexible,
60
+ registry: Optional[JWERegistry]=None) -> EncryptionData:
61
+
62
+ value = to_bytes(value)
63
+ obj = extract_compact(value)
64
+ if registry is None:
65
+ registry = default_registry
66
+
67
+ recipient = obj.recipients[0]
68
+ key = guess_key(private_key, recipient)
69
+ recipient.recipient_key = key
70
+ return perform_decrypt(obj, registry)
@@ -0,0 +1,86 @@
1
+ import random
2
+ from typing import Callable, Union, Any, Protocol
3
+ from .rfc7517 import (
4
+ SymmetricKey,
5
+ AsymmetricKey,
6
+ Key,
7
+ KeySet,
8
+ JWK_REGISTRY,
9
+ )
10
+ from .rfc7517.keygen import (
11
+ import_key,
12
+ generate_key,
13
+ )
14
+ from .rfc7517 import types
15
+ from .rfc7518.oct_key import OctKey
16
+ from .rfc7518.rsa_key import RSAKey
17
+ from .rfc7518.ec_key import ECKey
18
+ from .rfc8037.okp_key import OKPKey
19
+ from .rfc8812 import register_secp256k1
20
+ from .registry import Header
21
+
22
+
23
+ KeyCallable = Callable[[Any, bool], Key]
24
+ KeyFlexible = Union[Key, KeySet, KeyCallable]
25
+
26
+ __all__ = [
27
+ 'types',
28
+ 'JWK_REGISTRY',
29
+ 'SymmetricKey',
30
+ 'AsymmetricKey',
31
+ 'Key',
32
+ 'KeyCallable',
33
+ 'KeyFlexible',
34
+ 'OctKey',
35
+ 'RSAKey',
36
+ 'ECKey',
37
+ 'OKPKey',
38
+ 'KeySet',
39
+ 'generate_key',
40
+ 'import_key',
41
+ 'guess_key',
42
+ ]
43
+
44
+ # register thumbprint method
45
+ register_secp256k1()
46
+
47
+ # register all key types
48
+ JWK_REGISTRY[OctKey.key_type] = OctKey
49
+ JWK_REGISTRY[RSAKey.key_type] = RSAKey
50
+ JWK_REGISTRY[ECKey.key_type] = ECKey
51
+ JWK_REGISTRY[OKPKey.key_type] = OKPKey
52
+
53
+
54
+ class GuestProtocol(Protocol):
55
+ def headers(self) -> Header:
56
+ ...
57
+
58
+ def set_kid(self, kid: str):
59
+ ...
60
+
61
+
62
+ def guess_key(key: KeyFlexible, obj: GuestProtocol) -> Key:
63
+ """Guess key from a various sources.
64
+
65
+ :param key: a very flexible key
66
+ :param obj: a protocol that has ``headers`` and ``set_kid`` methods
67
+ """
68
+ if isinstance(key, (SymmetricKey, AsymmetricKey)):
69
+ return key
70
+
71
+ elif isinstance(key, KeySet):
72
+ headers = obj.headers()
73
+ kid = headers.get('kid')
74
+
75
+ if not kid:
76
+ # choose one key by random
77
+ key: Key = random.choice(key.keys)
78
+ # use side effect to add kid information
79
+ obj.set_kid(key.kid)
80
+ return key
81
+ return key.get_by_kid(kid)
82
+
83
+ elif callable(key):
84
+ return key(obj)
85
+
86
+ raise ValueError("Invalid key")
@@ -0,0 +1,192 @@
1
+ from typing import Optional, AnyStr, List, Union
2
+ from .rfc7515 import types
3
+ from .rfc7515.model import JWSAlgModel
4
+ from .rfc7515.registry import (
5
+ JWSRegistry,
6
+ default_registry,
7
+ )
8
+ from .rfc7515.compact import (
9
+ sign_compact,
10
+ extract_compact,
11
+ verify_compact,
12
+ )
13
+ from .rfc7515.json import (
14
+ sign_json,
15
+ verify_json,
16
+ extract_json,
17
+ )
18
+ from .rfc7515.types import (
19
+ Header,
20
+ HeaderMember,
21
+ HeaderDict,
22
+ SignatureData,
23
+ JSONSerialization,
24
+ )
25
+ from .rfc7518.jws_algs import JWS_ALGORITHMS
26
+ from .rfc8037.jws_eddsa import EdDSA
27
+ from .rfc8812 import ES256K
28
+ from .errors import BadSignatureError
29
+ from .jwk import Key, KeyFlexible, guess_key
30
+ from .util import to_bytes, urlsafe_b64encode
31
+
32
+ __all__ = [
33
+ 'types',
34
+ 'JWSAlgModel',
35
+ 'JWSRegistry',
36
+ 'SignatureData',
37
+
38
+ 'serialize_compact',
39
+ 'deserialize_compact',
40
+ 'extract_compact',
41
+ 'validate_compact',
42
+
43
+ 'serialize_json',
44
+ 'deserialize_json',
45
+ 'extract_json',
46
+ 'validate_json',
47
+ ]
48
+
49
+ # register supported alg models
50
+ def __register():
51
+ # register alg in RFC7518
52
+ for _alg in JWS_ALGORITHMS:
53
+ JWSRegistry.register(_alg)
54
+ # register alg in RFC8037
55
+ JWSRegistry.register(EdDSA)
56
+ # register alg in RFC8812
57
+ JWSRegistry.register(ES256K)
58
+
59
+ __register()
60
+
61
+
62
+ def serialize_compact(
63
+ header: Header,
64
+ payload: bytes,
65
+ key: KeyFlexible,
66
+ registry: Optional[JWSRegistry]=None) -> bytes:
67
+ """Generate a JWS Compact Serialization. The JWS Compact Serialization
68
+ represents digitally signed or MACed content as a compact, URL-safe
69
+ string, per Section 7.1.
70
+
71
+ .. code-block:: text
72
+
73
+ BASE64URL(UTF8(JWS Protected Header)) || '.' ||
74
+ BASE64URL(JWS Payload) || '.' ||
75
+ BASE64URL(JWS Signature)
76
+
77
+ :param header: protected header part of the JWS, in dict
78
+ :param payload: payload data of the JWS, in bytes
79
+ :param key: a flexible private key to sign the signature
80
+ :param registry: a JWSRegistry to use
81
+ :return: JWS in bytes
82
+
83
+ .. note:: The returned value is in bytes
84
+ """
85
+ if registry is None:
86
+ registry = default_registry
87
+
88
+ registry.check_header(header)
89
+ member = HeaderMember(header)
90
+ obj = SignatureData([member], payload)
91
+ obj.compact = True
92
+ alg: JWSAlgModel = registry.get_alg(header['alg'])
93
+ key: Key = guess_key(key, member)
94
+ return sign_compact(obj, alg, key)
95
+
96
+
97
+ def validate_compact(
98
+ obj: SignatureData,
99
+ key: KeyFlexible,
100
+ registry: Optional[JWSRegistry]=None):
101
+ """Validate the JWS Compact Serialization with the given key.
102
+ This method is usually used together with ``extract_compact``.
103
+
104
+ :param obj: object of the JWS Compact Serialization
105
+ :param key: a flexible public key to verify the signature
106
+ :param registry: a JWSRegistry to use
107
+ :raise: ValueError or BadSignatureError
108
+ """
109
+ if registry is None:
110
+ registry = default_registry
111
+ member = obj.members[0]
112
+ alg: JWSAlgModel = registry.get_alg(member.protected['alg'])
113
+ key: Key = guess_key(key, member)
114
+ if not verify_compact(obj, alg, key):
115
+ raise BadSignatureError()
116
+
117
+
118
+ def deserialize_compact(
119
+ value: AnyStr,
120
+ key: KeyFlexible,
121
+ registry: Optional[JWSRegistry]=None) -> SignatureData:
122
+ """Extract and validate the JWS (in string) with the given key.
123
+
124
+ :param value: a string (or bytes) of the JWS
125
+ :param key: a flexible public key to verify the signature
126
+ :param registry: a JWSRegistry to use
127
+ :return: object of the JWS Compact Serialization
128
+ """
129
+ obj = extract_compact(to_bytes(value))
130
+ validate_compact(obj, key, registry)
131
+ return obj
132
+
133
+
134
+ def serialize_json(
135
+ members: Union[HeaderDict, List[HeaderDict]],
136
+ payload: bytes,
137
+ key: KeyFlexible,
138
+ registry: Optional[JWSRegistry]=None) -> JSONSerialization:
139
+ if registry is None:
140
+ registry = default_registry
141
+
142
+ if isinstance(members, dict):
143
+ flatten = True
144
+ registry.check_header(members['protected'])
145
+ members = [members]
146
+ else:
147
+ flatten = False
148
+ for member in members:
149
+ registry.check_header(member['protected'])
150
+
151
+ members = [HeaderMember(**member) for member in members]
152
+ obj = SignatureData(members, payload)
153
+ obj.segments['payload'] = urlsafe_b64encode(payload)
154
+ obj.flatten = flatten
155
+
156
+ find_key = lambda d: guess_key(key, d)
157
+ return sign_json(obj, registry.get_alg, find_key)
158
+
159
+
160
+ def validate_json(
161
+ obj: SignatureData,
162
+ key: KeyFlexible,
163
+ registry: Optional[JWSRegistry]=None):
164
+ """Validate the JWS JSON Serialization with the given key.
165
+ This method is usually used together with ``extract_json``.
166
+
167
+ :param obj: object of the JWS JSON Serialization
168
+ :param key: a flexible public key to verify the signature
169
+ :param registry: a JWSRegistry to use
170
+ :raise: ValueError or BadSignatureError
171
+ """
172
+ if registry is None:
173
+ registry = default_registry
174
+ find_key = lambda d: guess_key(key, d)
175
+ if not verify_json(obj, registry.get_alg, find_key):
176
+ raise BadSignatureError()
177
+
178
+
179
+ def deserialize_json(
180
+ value: JSONSerialization,
181
+ key: KeyFlexible,
182
+ registry: Optional[JWSRegistry]=None) -> SignatureData:
183
+ """Extract and validate the JWS (in string) with the given key.
184
+
185
+ :param value: a dict of the JSON signature
186
+ :param key: a flexible public key to verify the signature
187
+ :param registry: a JWSRegistry to use
188
+ :return: object of the SignatureData
189
+ """
190
+ obj = extract_json(value)
191
+ validate_json(obj, key, registry)
192
+ return obj
@@ -0,0 +1,79 @@
1
+ from typing import Optional, AnyStr
2
+ from .rfc7515.compact import extract_compact
3
+ from .rfc7519.claims import Claims, convert_claims
4
+ from .rfc7519.validators import ClaimsOption, JWTClaimsRequests
5
+ from .rfc7519.registry import JWTRegistry, default_registry
6
+ from .jws import serialize_compact, validate_compact
7
+ from .jwk import KeyFlexible
8
+ from .errors import InvalidTypeError, InvalidPayloadError
9
+ from .util import to_bytes
10
+ from .registry import Header
11
+
12
+
13
+ __all__ = [
14
+ 'Header',
15
+ 'Claims',
16
+ 'Token',
17
+ 'ClaimsOption',
18
+ 'JWTRegistry',
19
+ 'JWTClaimsRequests',
20
+ 'encode',
21
+ 'decode',
22
+ ]
23
+
24
+
25
+ class Token:
26
+ def __init__(self, header: Header, claims: Claims):
27
+ self.header = header
28
+ self.claims = claims
29
+
30
+
31
+ def encode(
32
+ header: Header,
33
+ claims: Claims,
34
+ key: KeyFlexible,
35
+ registry: Optional[JWTRegistry]=None) -> str:
36
+ """Encode a JSON Web Token with the given header, and claims.
37
+
38
+ :param header: A dict of the JWT header
39
+ :param claims: A dict of the JWT claims to be encoded
40
+ :param key: key used to sign the signature
41
+ :param registry: a JWTRegistry to use
42
+ """
43
+ # add ``typ`` in header
44
+ header['typ'] = 'JWT'
45
+ payload = convert_claims(claims)
46
+ if registry is None:
47
+ registry = default_registry
48
+ result = serialize_compact(header, payload, key, registry)
49
+ return result.decode('utf-8')
50
+
51
+
52
+ def decode(
53
+ value: AnyStr,
54
+ key: KeyFlexible,
55
+ registry: Optional[JWTRegistry]=None) -> Token:
56
+ """Decode the JSON Web Token string with the given key, and validate
57
+ it with the claims requests. This method is a combination of the
58
+ :function:`extract` and :function:`validate`.
59
+
60
+ :param value: text of the JWT
61
+ :param key: key used to verify the signature
62
+ :param registry: a JWTRegistry to use
63
+ :raise: BadSignatureError
64
+ """
65
+ obj = extract_compact(to_bytes(value))
66
+ try:
67
+ token = Token(obj.headers(), obj.claims)
68
+ except ValueError:
69
+ raise InvalidPayloadError('Payload should be a JSON dict')
70
+ if registry is None:
71
+ registry = default_registry
72
+
73
+ typ = token.header.get('typ')
74
+ if typ and typ != 'JWT':
75
+ raise InvalidTypeError()
76
+
77
+ registry.check_claims(token.claims)
78
+ validate_compact(obj, key, registry)
79
+ return token