Repository: jmwri/simplejwt

Issue:
Add support for registered claims
There are a list of registered claims in [rfc7519](https://tools.ietf.org/html/rfc7519#section-4.1).

Simplejwt should support these out of the box.

Discussion:

Patch:
diff --git a/README.md b/README.md
index cc0b04c..2b7b764 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,30 @@ token = encode('secret', {'my_payload': 'some_data'}, 'HS256')
 | `payload` | `dict` | *N/A* | The payload data contained within the token. |
 | `alg` | `int` | `HS256` | The algorithm to use to create the token. |
 
+## Make
+Returns a new token. This function has arguments for registered claims as specified in [rfc7519](https://tools.ietf.org/html/rfc7519#section-4.1).
+
+Any registered claims provided in the payload will take precedence over any provided as arguments. 
+
+```
+from simplejwt import make
+token = make('secret', {'my_payload': 'some_data'}, 'HS256', issuer='acme', valid_to=1234567)
+# eyJ0eXBlIjogIkpXVCIsICJhbGciOiAiSFMyNTYifQ.eyJteV9wYXlsb2FkIjogInNvbWVfZGF0YSIsICJpc3MiOiAiYWNtZSIsICJleHAiOiAxMjM0NTY3fQ.Nr5IADzsOhlzjxnghquBrRwewg10srDHu__-HN7GGGA
+```
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| `secret` | `str` | *N/A* | The secret used to create the token. |
+| `payload` | `dict` | *N/A* | The payload data contained within the token. |
+| `alg` | `int` | `HS256` | The algorithm to use to create the token. |
+| `issuer` | `str` | `None` | The issuer of the token. |
+| `subject` | `str` | `None` | The subject of the token. |
+| `audience` | `str` | `None` | The audience of the token. |
+| `valid_to` | `int` | `None` | The expiry date of the token as a timestamp. |
+| `valid_from` | `int` | `None` | The date the token is valid from as a timestamp. |
+| `issued_at` | `int` | `None` | The date the token was issued as a timestamp. |
+| `id` | `str` | `None` | The id of the token. |
+
 ## Decode
 Returns the payload from a token.
 
diff --git a/simplejwt/__init__.py b/simplejwt/__init__.py
index 774ddc8..1fa6839 100644
--- a/simplejwt/__init__.py
+++ b/simplejwt/__init__.py
@@ -1,1 +1,1 @@
-from simplejwt.jwt import encode, decode
+from simplejwt.jwt import encode, make, decode
diff --git a/simplejwt/jwt.py b/simplejwt/jwt.py
index 5645302..c776ec4 100644
--- a/simplejwt/jwt.py
+++ b/simplejwt/jwt.py
@@ -26,6 +26,28 @@ def _hash(secret: bytes, data: bytes, alg: str) -> bytes:
         .digest()
 
 
+def make(secret: Union[str, bytes], payload: dict, alg='HS256',
+         issuer: str = None, subject: str = None, audience: str = None,
+         valid_to: int = None, valid_from: int = None, issued_at: int = None,
+         id: str = None):
+    new_payload = payload.copy()
+    if issuer and 'iss' not in new_payload:
+        new_payload['iss'] = issuer
+    if subject and 'sub' not in new_payload:
+        new_payload['sub'] = subject
+    if audience and 'aud' not in new_payload:
+        new_payload['aud'] = audience
+    if valid_to and 'exp' not in new_payload:
+        new_payload['exp'] = valid_to
+    if valid_from and 'nbf' not in new_payload:
+        new_payload['nbf'] = valid_from
+    if issued_at and 'iat' not in new_payload:
+        new_payload['iat'] = issued_at
+    if id and 'jti' not in new_payload:
+        new_payload['jti'] = id
+    return encode(secret, new_payload, alg)
+
+
 def encode(secret: Union[str, bytes], payload: dict, alg='HS256'):
     secret = util.to_bytes(secret)
 


Test patch:
diff --git a/tests/test_jwt.py b/tests/test_jwt.py
index ebb62f9..cacda88 100644
--- a/tests/test_jwt.py
+++ b/tests/test_jwt.py
@@ -25,6 +25,26 @@ test_token_data = {
     }
 }
 
+registered_claims = {
+    'issuer': 'iss',
+    'subject': 'sub',
+    'audience': 'aud',
+    'valid_to': 'exp',
+    'valid_from': 'nbf',
+    'issued_at': 'iat',
+    'id': 'jti',
+}
+
+test_registered_claims = {
+    'issuer': 'test_issuer',
+    'subject': 'test_subject',
+    'audience': 'test_audience',
+    'valid_to': 789,
+    'valid_from': 456,
+    'issued_at': 123,
+    'id': 'test_id',
+}
+
 
 def test_get_algorithm_hs256():
     assert jwt.get_algorithm('HS256') is hashlib.sha256
@@ -63,6 +83,31 @@ def test_encode():
         ) == token
 
 
+def test_make_claims():
+    for name, abb in registered_claims.items():
+        args = {
+            'secret': test_token_data['secret'],
+            'payload': test_token_data['payload'],
+            name: test_registered_claims[name]
+        }
+        token = jwt.make(**args)
+        payload = jwt.decode(test_token_data['secret'], token)
+        assert payload[abb] == test_registered_claims[name]
+
+
+def test_make_precedence():
+    token = jwt.make(test_token_data['secret'], {'iss': 'usr_defined_iss'},
+                     issuer='my_iss')
+    payload = jwt.decode(test_token_data['secret'], token)
+    assert payload['iss'] == 'usr_defined_iss'
+
+
+def test_make_leaves_payload_unmodified():
+    payload = {'my': 'payload'}
+    jwt.make(test_token_data['secret'], payload, issuer='my_iss')
+    assert payload == {'my': 'payload'}
+
+
 def test_decode():
     for alg, token in test_tokens.items():
         assert jwt.decode(