We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: Missing "app id" in app extension token
### What are you trying to achieve?

App (via app-sdk) is checking `app` field in user JWT token (sent from dashboard, via postmessage to appbridge) and compares it to registered app ID (that app persists). If app ID is different, app is rejecting the call.

However, there are also extensions that have their own token (they can have different permissions). **This token lacks `app` field**. 

For app, extension is its part, so its expected to have app ID in the token as well. Otherwise, app would require to persist all registered extensions to work, which is quite hard to implement.

### Steps to reproduce the problem

## App token payload

```json
{
  "iat": 1679048767,
  "owner": "saleor",
  "iss": "https://automation-dashboard.staging.saleor.cloud/graphql/",
  "exp": 1679135167,
  "token": "M2irFmzVASR3",
  "email": "testers+dashboard@saleor.io",
  "type": "thirdparty",
  "user_id": "VXNlcjoxMDMz",
  "is_staff": true,
  "app": "QXBwOjY0",
  "permissions": [
    "MANAGE_PRODUCTS"
  ],
  "user_permissions": [
    "MANAGE_GIFT_CARD",
    "MANAGE_MENUS",
    "MANAGE_PAGES",
    "MANAGE_PAGE_TYPES_AND_ATTRIBUTES",
    "MANAGE_PLUGINS",
    "MANAGE_TAXES",
    "MANAGE_USERS",
    "MANAGE_CHECKOUTS",
    "MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES",
    "MANAGE_TRANSLATIONS",
    "MANAGE_APPS",
    "MANAGE_OBSERVABILITY",
    "MANAGE_STAFF",
    "HANDLE_TAXES",
    "MANAGE_CHANNELS",
    "HANDLE_CHECKOUTS",
    "MANAGE_SETTINGS",
    "HANDLE_PAYMENTS",
    "MANAGE_ORDERS",
    "MANAGE_PRODUCTS",
    "MANAGE_SHIPPING",
    "MANAGE_DISCOUNTS",
    "IMPERSONATE_USER"
  ]
}
```


## Extension token payload

```json
  "owner": "saleor",
  "iss": "https://automation-dashboard.staging.saleor.cloud/graphql/",
  "exp": 1679137338,
  "token": "M2irFmzVASR3",
  "email": "testers+dashboard@saleor.io",
  "type": "thirdparty",
  "user_id": "VXNlcjoxMDMz",
  "is_staff": true,
  "app_extension": "QXBwRXh0ZW5zaW9uOjQy",
  "permissions": [],
  "user_permissions": [
    "MANAGE_GIFT_CARD",
    "MANAGE_MENUS",
    "MANAGE_PAGES",
    "MANAGE_PAGE_TYPES_AND_ATTRIBUTES",
    "MANAGE_PLUGINS",
    "MANAGE_TAXES",
    "MANAGE_USERS",
    "MANAGE_CHECKOUTS",
    "MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES",
    "MANAGE_TRANSLATIONS",
    "MANAGE_APPS",
    "MANAGE_OBSERVABILITY",
    "MANAGE_STAFF",
    "HANDLE_TAXES",
    "MANAGE_CHANNELS",
    "HANDLE_CHECKOUTS",
    "MANAGE_SETTINGS",
    "HANDLE_PAYMENTS",
    "MANAGE_ORDERS",
    "MANAGE_PRODUCTS",
    "MANAGE_SHIPPING",
    "MANAGE_DISCOUNTS",
    "IMPERSONATE_USER"
  ]
}
```

### What did you expect to happen?

Extension token should contain `app` field

### Logs

_No response_

### Environment

Saleor version: …
OS and version: …


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/core/jwt.py`
Content:
```
1 from datetime import datetime, timedelta
2 from typing import Any, Dict, Iterable, Optional
3 
4 import graphene
5 import jwt
6 from django.conf import settings
7 
8 from ..account.models import User
9 from ..app.models import App, AppExtension
10 from ..permission.enums import (
11     get_permission_names,
12     get_permissions_from_codenames,
13     get_permissions_from_names,
14 )
15 from ..permission.models import Permission
16 from .jwt_manager import get_jwt_manager
17 
18 JWT_ACCESS_TYPE = "access"
19 JWT_REFRESH_TYPE = "refresh"
20 JWT_THIRDPARTY_ACCESS_TYPE = "thirdparty"
21 JWT_REFRESH_TOKEN_COOKIE_NAME = "refreshToken"
22 
23 PERMISSIONS_FIELD = "permissions"
24 USER_PERMISSION_FIELD = "user_permissions"
25 JWT_SALEOR_OWNER_NAME = "saleor"
26 JWT_OWNER_FIELD = "owner"
27 
28 
29 def jwt_base_payload(
30     exp_delta: Optional[timedelta], token_owner: str
31 ) -> Dict[str, Any]:
32     utc_now = datetime.utcnow()
33 
34     payload = {
35         "iat": utc_now,
36         JWT_OWNER_FIELD: token_owner,
37         "iss": get_jwt_manager().get_issuer(),
38     }
39     if exp_delta:
40         payload["exp"] = utc_now + exp_delta
41     return payload
42 
43 
44 def jwt_user_payload(
45     user: User,
46     token_type: str,
47     exp_delta: Optional[timedelta],
48     additional_payload: Optional[Dict[str, Any]] = None,
49     token_owner: str = JWT_SALEOR_OWNER_NAME,
50 ) -> Dict[str, Any]:
51     payload = jwt_base_payload(exp_delta, token_owner)
52     payload.update(
53         {
54             "token": user.jwt_token_key,
55             "email": user.email,
56             "type": token_type,
57             "user_id": graphene.Node.to_global_id("User", user.id),
58             "is_staff": user.is_staff,
59         }
60     )
61     if additional_payload:
62         payload.update(additional_payload)
63     return payload
64 
65 
66 def jwt_encode(payload: Dict[str, Any]) -> str:
67     jwt_manager = get_jwt_manager()
68     return jwt_manager.encode(payload)
69 
70 
71 def jwt_decode_with_exception_handler(
72     token: str, verify_expiration=settings.JWT_EXPIRE
73 ) -> Optional[Dict[str, Any]]:
74     try:
75         return jwt_decode(token, verify_expiration=verify_expiration)
76     except jwt.PyJWTError:
77         return None
78 
79 
80 def jwt_decode(
81     token: str, verify_expiration=settings.JWT_EXPIRE, verify_aud: bool = False
82 ) -> Dict[str, Any]:
83     jwt_manager = get_jwt_manager()
84     return jwt_manager.decode(token, verify_expiration, verify_aud=verify_aud)
85 
86 
87 def create_token(payload: Dict[str, Any], exp_delta: timedelta) -> str:
88     payload.update(jwt_base_payload(exp_delta, token_owner=JWT_SALEOR_OWNER_NAME))
89     return jwt_encode(payload)
90 
91 
92 def create_access_token(
93     user: User, additional_payload: Optional[Dict[str, Any]] = None
94 ) -> str:
95     payload = jwt_user_payload(
96         user, JWT_ACCESS_TYPE, settings.JWT_TTL_ACCESS, additional_payload
97     )
98     return jwt_encode(payload)
99 
100 
101 def create_refresh_token(
102     user: User, additional_payload: Optional[Dict[str, Any]] = None
103 ) -> str:
104     payload = jwt_user_payload(
105         user,
106         JWT_REFRESH_TYPE,
107         settings.JWT_TTL_REFRESH,
108         additional_payload,
109     )
110     return jwt_encode(payload)
111 
112 
113 def get_user_from_payload(payload: Dict[str, Any], request=None) -> Optional[User]:
114     # TODO: dataloader
115     user = User.objects.filter(email=payload["email"], is_active=True).first()
116     user_jwt_token = payload.get("token")
117     if not user_jwt_token or not user:
118         raise jwt.InvalidTokenError(
119             "Invalid token. Create new one by using tokenCreate mutation."
120         )
121     if user.jwt_token_key != user_jwt_token:
122         raise jwt.InvalidTokenError(
123             "Invalid token. Create new one by using tokenCreate mutation."
124         )
125     return user
126 
127 
128 def is_saleor_token(token: str) -> bool:
129     """Confirm that token was generated by Saleor not by plugin."""
130     try:
131         payload = jwt.decode(token, options={"verify_signature": False})
132     except jwt.PyJWTError:
133         return False
134     owner = payload.get(JWT_OWNER_FIELD)
135     if not owner or owner != JWT_SALEOR_OWNER_NAME:
136         return False
137     return True
138 
139 
140 def get_user_from_access_payload(payload: dict, request=None) -> Optional[User]:
141     jwt_type = payload.get("type")
142     if jwt_type not in [JWT_ACCESS_TYPE, JWT_THIRDPARTY_ACCESS_TYPE]:
143         raise jwt.InvalidTokenError(
144             "Invalid token. Create new one by using tokenCreate mutation."
145         )
146     permissions = payload.get(PERMISSIONS_FIELD, None)
147     user = get_user_from_payload(payload, request)
148     if user:
149         if permissions is not None:
150             token_permissions = get_permissions_from_names(permissions)
151             token_codenames = [perm.codename for perm in token_permissions]
152             user.effective_permissions = get_permissions_from_codenames(token_codenames)
153             user.is_staff = True if user.effective_permissions else False
154 
155         if payload.get("is_staff"):
156             user.is_staff = True
157     return user
158 
159 
160 def _create_access_token_for_third_party_actions(
161     permissions: Iterable["Permission"],
162     user: "User",
163     type: str,
164     object_id: int,
165     object_payload_key: str,
166     audience: Optional[str],
167 ):
168     app_permission_enums = get_permission_names(permissions)
169 
170     permissions = user.effective_permissions
171     user_permission_enums = get_permission_names(permissions)
172     additional_payload = {
173         object_payload_key: graphene.Node.to_global_id(type, object_id),
174         PERMISSIONS_FIELD: list(app_permission_enums & user_permission_enums),
175         USER_PERMISSION_FIELD: list(user_permission_enums),
176     }
177     if audience:
178         additional_payload["aud"] = audience
179 
180     payload = jwt_user_payload(
181         user,
182         JWT_THIRDPARTY_ACCESS_TYPE,
183         exp_delta=settings.JWT_TTL_APP_ACCESS,
184         additional_payload=additional_payload,
185     )
186     return jwt_encode(payload)
187 
188 
189 def create_access_token_for_app(app: "App", user: "User"):
190     """Create access token for app.
191 
192     App can use user's JWT token to proceed given operation in Saleor.
193     The token which can be used by App has additional field defining the permissions
194     assigned to it. The permissions set is the intersection of user permissions and
195     app permissions.
196     """
197     app_permissions = app.permissions.all()
198     return _create_access_token_for_third_party_actions(
199         permissions=app_permissions,
200         user=user,
201         type="App",
202         object_id=app.id,
203         object_payload_key="app",
204         audience=app.audience,
205     )
206 
207 
208 def create_access_token_for_app_extension(
209     app_extension: "AppExtension",
210     permissions: Iterable["Permission"],
211     user: "User",
212     app: "App",
213 ):
214     return _create_access_token_for_third_party_actions(
215         permissions=permissions,
216         user=user,
217         type="AppExtension",
218         object_id=app_extension.id,
219         object_payload_key="app_extension",
220         audience=app.audience,
221     )
222
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/saleor/core/jwt.py b/saleor/core/jwt.py
--- a/saleor/core/jwt.py
+++ b/saleor/core/jwt.py
@@ -20,6 +20,7 @@
 JWT_THIRDPARTY_ACCESS_TYPE = "thirdparty"
 JWT_REFRESH_TOKEN_COOKIE_NAME = "refreshToken"
 
+APP_KEY_FIELD = "app"
 PERMISSIONS_FIELD = "permissions"
 USER_PERMISSION_FIELD = "user_permissions"
 JWT_SALEOR_OWNER_NAME = "saleor"
@@ -160,22 +161,22 @@
 def _create_access_token_for_third_party_actions(
     permissions: Iterable["Permission"],
     user: "User",
-    type: str,
-    object_id: int,
-    object_payload_key: str,
-    audience: Optional[str],
+    app: "App",
+    extra: Optional[Dict[str, Any]] = None,
 ):
     app_permission_enums = get_permission_names(permissions)
 
     permissions = user.effective_permissions
     user_permission_enums = get_permission_names(permissions)
     additional_payload = {
-        object_payload_key: graphene.Node.to_global_id(type, object_id),
+        APP_KEY_FIELD: graphene.Node.to_global_id("App", app.id),
         PERMISSIONS_FIELD: list(app_permission_enums & user_permission_enums),
         USER_PERMISSION_FIELD: list(user_permission_enums),
     }
-    if audience:
-        additional_payload["aud"] = audience
+    if app.audience:
+        additional_payload["aud"] = app.audience
+    if extra:
+        additional_payload.update(extra)
 
     payload = jwt_user_payload(
         user,
@@ -196,12 +197,7 @@
     """
     app_permissions = app.permissions.all()
     return _create_access_token_for_third_party_actions(
-        permissions=app_permissions,
-        user=user,
-        type="App",
-        object_id=app.id,
-        object_payload_key="app",
-        audience=app.audience,
+        permissions=app_permissions, user=user, app=app
     )
 
 
@@ -211,11 +207,10 @@
     user: "User",
     app: "App",
 ):
+    app_extension_id = graphene.Node.to_global_id("AppExtension", app_extension.id)
     return _create_access_token_for_third_party_actions(
         permissions=permissions,
         user=user,
-        type="AppExtension",
-        object_id=app_extension.id,
-        object_payload_key="app_extension",
-        audience=app.audience,
+        app=app,
+        extra={"app_extension": app_extension_id},
     )

{"golden_diff": "diff --git a/saleor/core/jwt.py b/saleor/core/jwt.py\n--- a/saleor/core/jwt.py\n+++ b/saleor/core/jwt.py\n@@ -20,6 +20,7 @@\n JWT_THIRDPARTY_ACCESS_TYPE = \"thirdparty\"\n JWT_REFRESH_TOKEN_COOKIE_NAME = \"refreshToken\"\n \n+APP_KEY_FIELD = \"app\"\n PERMISSIONS_FIELD = \"permissions\"\n USER_PERMISSION_FIELD = \"user_permissions\"\n JWT_SALEOR_OWNER_NAME = \"saleor\"\n@@ -160,22 +161,22 @@\n def _create_access_token_for_third_party_actions(\n     permissions: Iterable[\"Permission\"],\n     user: \"User\",\n-    type: str,\n-    object_id: int,\n-    object_payload_key: str,\n-    audience: Optional[str],\n+    app: \"App\",\n+    extra: Optional[Dict[str, Any]] = None,\n ):\n     app_permission_enums = get_permission_names(permissions)\n \n     permissions = user.effective_permissions\n     user_permission_enums = get_permission_names(permissions)\n     additional_payload = {\n-        object_payload_key: graphene.Node.to_global_id(type, object_id),\n+        APP_KEY_FIELD: graphene.Node.to_global_id(\"App\", app.id),\n         PERMISSIONS_FIELD: list(app_permission_enums & user_permission_enums),\n         USER_PERMISSION_FIELD: list(user_permission_enums),\n     }\n-    if audience:\n-        additional_payload[\"aud\"] = audience\n+    if app.audience:\n+        additional_payload[\"aud\"] = app.audience\n+    if extra:\n+        additional_payload.update(extra)\n \n     payload = jwt_user_payload(\n         user,\n@@ -196,12 +197,7 @@\n     \"\"\"\n     app_permissions = app.permissions.all()\n     return _create_access_token_for_third_party_actions(\n-        permissions=app_permissions,\n-        user=user,\n-        type=\"App\",\n-        object_id=app.id,\n-        object_payload_key=\"app\",\n-        audience=app.audience,\n+        permissions=app_permissions, user=user, app=app\n     )\n \n \n@@ -211,11 +207,10 @@\n     user: \"User\",\n     app: \"App\",\n ):\n+    app_extension_id = graphene.Node.to_global_id(\"AppExtension\", app_extension.id)\n     return _create_access_token_for_third_party_actions(\n         permissions=permissions,\n         user=user,\n-        type=\"AppExtension\",\n-        object_id=app_extension.id,\n-        object_payload_key=\"app_extension\",\n-        audience=app.audience,\n+        app=app,\n+        extra={\"app_extension\": app_extension_id},\n     )\n", "issue": "Bug: Missing \"app id\" in app extension token\n### What are you trying to achieve?\r\n\r\nApp (via app-sdk) is checking `app` field in user JWT token (sent from dashboard, via postmessage to appbridge) and compares it to registered app ID (that app persists). If app ID is different, app is rejecting the call.\r\n\r\nHowever, there are also extensions that have their own token (they can have different permissions). **This token lacks `app` field**. \r\n\r\nFor app, extension is its part, so its expected to have app ID in the token as well. Otherwise, app would require to persist all registered extensions to work, which is quite hard to implement.\r\n\r\n### Steps to reproduce the problem\r\n\r\n## App token payload\r\n\r\n```json\r\n{\r\n  \"iat\": 1679048767,\r\n  \"owner\": \"saleor\",\r\n  \"iss\": \"https://automation-dashboard.staging.saleor.cloud/graphql/\",\r\n  \"exp\": 1679135167,\r\n  \"token\": \"M2irFmzVASR3\",\r\n  \"email\": \"testers+dashboard@saleor.io\",\r\n  \"type\": \"thirdparty\",\r\n  \"user_id\": \"VXNlcjoxMDMz\",\r\n  \"is_staff\": true,\r\n  \"app\": \"QXBwOjY0\",\r\n  \"permissions\": [\r\n    \"MANAGE_PRODUCTS\"\r\n  ],\r\n  \"user_permissions\": [\r\n    \"MANAGE_GIFT_CARD\",\r\n    \"MANAGE_MENUS\",\r\n    \"MANAGE_PAGES\",\r\n    \"MANAGE_PAGE_TYPES_AND_ATTRIBUTES\",\r\n    \"MANAGE_PLUGINS\",\r\n    \"MANAGE_TAXES\",\r\n    \"MANAGE_USERS\",\r\n    \"MANAGE_CHECKOUTS\",\r\n    \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\",\r\n    \"MANAGE_TRANSLATIONS\",\r\n    \"MANAGE_APPS\",\r\n    \"MANAGE_OBSERVABILITY\",\r\n    \"MANAGE_STAFF\",\r\n    \"HANDLE_TAXES\",\r\n    \"MANAGE_CHANNELS\",\r\n    \"HANDLE_CHECKOUTS\",\r\n    \"MANAGE_SETTINGS\",\r\n    \"HANDLE_PAYMENTS\",\r\n    \"MANAGE_ORDERS\",\r\n    \"MANAGE_PRODUCTS\",\r\n    \"MANAGE_SHIPPING\",\r\n    \"MANAGE_DISCOUNTS\",\r\n    \"IMPERSONATE_USER\"\r\n  ]\r\n}\r\n```\r\n\r\n\r\n## Extension token payload\r\n\r\n```json\r\n  \"owner\": \"saleor\",\r\n  \"iss\": \"https://automation-dashboard.staging.saleor.cloud/graphql/\",\r\n  \"exp\": 1679137338,\r\n  \"token\": \"M2irFmzVASR3\",\r\n  \"email\": \"testers+dashboard@saleor.io\",\r\n  \"type\": \"thirdparty\",\r\n  \"user_id\": \"VXNlcjoxMDMz\",\r\n  \"is_staff\": true,\r\n  \"app_extension\": \"QXBwRXh0ZW5zaW9uOjQy\",\r\n  \"permissions\": [],\r\n  \"user_permissions\": [\r\n    \"MANAGE_GIFT_CARD\",\r\n    \"MANAGE_MENUS\",\r\n    \"MANAGE_PAGES\",\r\n    \"MANAGE_PAGE_TYPES_AND_ATTRIBUTES\",\r\n    \"MANAGE_PLUGINS\",\r\n    \"MANAGE_TAXES\",\r\n    \"MANAGE_USERS\",\r\n    \"MANAGE_CHECKOUTS\",\r\n    \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\",\r\n    \"MANAGE_TRANSLATIONS\",\r\n    \"MANAGE_APPS\",\r\n    \"MANAGE_OBSERVABILITY\",\r\n    \"MANAGE_STAFF\",\r\n    \"HANDLE_TAXES\",\r\n    \"MANAGE_CHANNELS\",\r\n    \"HANDLE_CHECKOUTS\",\r\n    \"MANAGE_SETTINGS\",\r\n    \"HANDLE_PAYMENTS\",\r\n    \"MANAGE_ORDERS\",\r\n    \"MANAGE_PRODUCTS\",\r\n    \"MANAGE_SHIPPING\",\r\n    \"MANAGE_DISCOUNTS\",\r\n    \"IMPERSONATE_USER\"\r\n  ]\r\n}\r\n```\r\n\r\n### What did you expect to happen?\r\n\r\nExtension token should contain `app` field\r\n\r\n### Logs\r\n\r\n_No response_\r\n\r\n### Environment\r\n\r\nSaleor version: \u2026\r\nOS and version: \u2026\r\n\n", "before_files": [{"content": "from datetime import datetime, timedelta\nfrom typing import Any, Dict, Iterable, Optional\n\nimport graphene\nimport jwt\nfrom django.conf import settings\n\nfrom ..account.models import User\nfrom ..app.models import App, AppExtension\nfrom ..permission.enums import (\n    get_permission_names,\n    get_permissions_from_codenames,\n    get_permissions_from_names,\n)\nfrom ..permission.models import Permission\nfrom .jwt_manager import get_jwt_manager\n\nJWT_ACCESS_TYPE = \"access\"\nJWT_REFRESH_TYPE = \"refresh\"\nJWT_THIRDPARTY_ACCESS_TYPE = \"thirdparty\"\nJWT_REFRESH_TOKEN_COOKIE_NAME = \"refreshToken\"\n\nPERMISSIONS_FIELD = \"permissions\"\nUSER_PERMISSION_FIELD = \"user_permissions\"\nJWT_SALEOR_OWNER_NAME = \"saleor\"\nJWT_OWNER_FIELD = \"owner\"\n\n\ndef jwt_base_payload(\n    exp_delta: Optional[timedelta], token_owner: str\n) -> Dict[str, Any]:\n    utc_now = datetime.utcnow()\n\n    payload = {\n        \"iat\": utc_now,\n        JWT_OWNER_FIELD: token_owner,\n        \"iss\": get_jwt_manager().get_issuer(),\n    }\n    if exp_delta:\n        payload[\"exp\"] = utc_now + exp_delta\n    return payload\n\n\ndef jwt_user_payload(\n    user: User,\n    token_type: str,\n    exp_delta: Optional[timedelta],\n    additional_payload: Optional[Dict[str, Any]] = None,\n    token_owner: str = JWT_SALEOR_OWNER_NAME,\n) -> Dict[str, Any]:\n    payload = jwt_base_payload(exp_delta, token_owner)\n    payload.update(\n        {\n            \"token\": user.jwt_token_key,\n            \"email\": user.email,\n            \"type\": token_type,\n            \"user_id\": graphene.Node.to_global_id(\"User\", user.id),\n            \"is_staff\": user.is_staff,\n        }\n    )\n    if additional_payload:\n        payload.update(additional_payload)\n    return payload\n\n\ndef jwt_encode(payload: Dict[str, Any]) -> str:\n    jwt_manager = get_jwt_manager()\n    return jwt_manager.encode(payload)\n\n\ndef jwt_decode_with_exception_handler(\n    token: str, verify_expiration=settings.JWT_EXPIRE\n) -> Optional[Dict[str, Any]]:\n    try:\n        return jwt_decode(token, verify_expiration=verify_expiration)\n    except jwt.PyJWTError:\n        return None\n\n\ndef jwt_decode(\n    token: str, verify_expiration=settings.JWT_EXPIRE, verify_aud: bool = False\n) -> Dict[str, Any]:\n    jwt_manager = get_jwt_manager()\n    return jwt_manager.decode(token, verify_expiration, verify_aud=verify_aud)\n\n\ndef create_token(payload: Dict[str, Any], exp_delta: timedelta) -> str:\n    payload.update(jwt_base_payload(exp_delta, token_owner=JWT_SALEOR_OWNER_NAME))\n    return jwt_encode(payload)\n\n\ndef create_access_token(\n    user: User, additional_payload: Optional[Dict[str, Any]] = None\n) -> str:\n    payload = jwt_user_payload(\n        user, JWT_ACCESS_TYPE, settings.JWT_TTL_ACCESS, additional_payload\n    )\n    return jwt_encode(payload)\n\n\ndef create_refresh_token(\n    user: User, additional_payload: Optional[Dict[str, Any]] = None\n) -> str:\n    payload = jwt_user_payload(\n        user,\n        JWT_REFRESH_TYPE,\n        settings.JWT_TTL_REFRESH,\n        additional_payload,\n    )\n    return jwt_encode(payload)\n\n\ndef get_user_from_payload(payload: Dict[str, Any], request=None) -> Optional[User]:\n    # TODO: dataloader\n    user = User.objects.filter(email=payload[\"email\"], is_active=True).first()\n    user_jwt_token = payload.get(\"token\")\n    if not user_jwt_token or not user:\n        raise jwt.InvalidTokenError(\n            \"Invalid token. Create new one by using tokenCreate mutation.\"\n        )\n    if user.jwt_token_key != user_jwt_token:\n        raise jwt.InvalidTokenError(\n            \"Invalid token. Create new one by using tokenCreate mutation.\"\n        )\n    return user\n\n\ndef is_saleor_token(token: str) -> bool:\n    \"\"\"Confirm that token was generated by Saleor not by plugin.\"\"\"\n    try:\n        payload = jwt.decode(token, options={\"verify_signature\": False})\n    except jwt.PyJWTError:\n        return False\n    owner = payload.get(JWT_OWNER_FIELD)\n    if not owner or owner != JWT_SALEOR_OWNER_NAME:\n        return False\n    return True\n\n\ndef get_user_from_access_payload(payload: dict, request=None) -> Optional[User]:\n    jwt_type = payload.get(\"type\")\n    if jwt_type not in [JWT_ACCESS_TYPE, JWT_THIRDPARTY_ACCESS_TYPE]:\n        raise jwt.InvalidTokenError(\n            \"Invalid token. Create new one by using tokenCreate mutation.\"\n        )\n    permissions = payload.get(PERMISSIONS_FIELD, None)\n    user = get_user_from_payload(payload, request)\n    if user:\n        if permissions is not None:\n            token_permissions = get_permissions_from_names(permissions)\n            token_codenames = [perm.codename for perm in token_permissions]\n            user.effective_permissions = get_permissions_from_codenames(token_codenames)\n            user.is_staff = True if user.effective_permissions else False\n\n        if payload.get(\"is_staff\"):\n            user.is_staff = True\n    return user\n\n\ndef _create_access_token_for_third_party_actions(\n    permissions: Iterable[\"Permission\"],\n    user: \"User\",\n    type: str,\n    object_id: int,\n    object_payload_key: str,\n    audience: Optional[str],\n):\n    app_permission_enums = get_permission_names(permissions)\n\n    permissions = user.effective_permissions\n    user_permission_enums = get_permission_names(permissions)\n    additional_payload = {\n        object_payload_key: graphene.Node.to_global_id(type, object_id),\n        PERMISSIONS_FIELD: list(app_permission_enums & user_permission_enums),\n        USER_PERMISSION_FIELD: list(user_permission_enums),\n    }\n    if audience:\n        additional_payload[\"aud\"] = audience\n\n    payload = jwt_user_payload(\n        user,\n        JWT_THIRDPARTY_ACCESS_TYPE,\n        exp_delta=settings.JWT_TTL_APP_ACCESS,\n        additional_payload=additional_payload,\n    )\n    return jwt_encode(payload)\n\n\ndef create_access_token_for_app(app: \"App\", user: \"User\"):\n    \"\"\"Create access token for app.\n\n    App can use user's JWT token to proceed given operation in Saleor.\n    The token which can be used by App has additional field defining the permissions\n    assigned to it. The permissions set is the intersection of user permissions and\n    app permissions.\n    \"\"\"\n    app_permissions = app.permissions.all()\n    return _create_access_token_for_third_party_actions(\n        permissions=app_permissions,\n        user=user,\n        type=\"App\",\n        object_id=app.id,\n        object_payload_key=\"app\",\n        audience=app.audience,\n    )\n\n\ndef create_access_token_for_app_extension(\n    app_extension: \"AppExtension\",\n    permissions: Iterable[\"Permission\"],\n    user: \"User\",\n    app: \"App\",\n):\n    return _create_access_token_for_third_party_actions(\n        permissions=permissions,\n        user=user,\n        type=\"AppExtension\",\n        object_id=app_extension.id,\n        object_payload_key=\"app_extension\",\n        audience=app.audience,\n    )\n", "path": "saleor/core/jwt.py"}], "after_files": [{"content": "from datetime import datetime, timedelta\nfrom typing import Any, Dict, Iterable, Optional\n\nimport graphene\nimport jwt\nfrom django.conf import settings\n\nfrom ..account.models import User\nfrom ..app.models import App, AppExtension\nfrom ..permission.enums import (\n    get_permission_names,\n    get_permissions_from_codenames,\n    get_permissions_from_names,\n)\nfrom ..permission.models import Permission\nfrom .jwt_manager import get_jwt_manager\n\nJWT_ACCESS_TYPE = \"access\"\nJWT_REFRESH_TYPE = \"refresh\"\nJWT_THIRDPARTY_ACCESS_TYPE = \"thirdparty\"\nJWT_REFRESH_TOKEN_COOKIE_NAME = \"refreshToken\"\n\nAPP_KEY_FIELD = \"app\"\nPERMISSIONS_FIELD = \"permissions\"\nUSER_PERMISSION_FIELD = \"user_permissions\"\nJWT_SALEOR_OWNER_NAME = \"saleor\"\nJWT_OWNER_FIELD = \"owner\"\n\n\ndef jwt_base_payload(\n    exp_delta: Optional[timedelta], token_owner: str\n) -> Dict[str, Any]:\n    utc_now = datetime.utcnow()\n\n    payload = {\n        \"iat\": utc_now,\n        JWT_OWNER_FIELD: token_owner,\n        \"iss\": get_jwt_manager().get_issuer(),\n    }\n    if exp_delta:\n        payload[\"exp\"] = utc_now + exp_delta\n    return payload\n\n\ndef jwt_user_payload(\n    user: User,\n    token_type: str,\n    exp_delta: Optional[timedelta],\n    additional_payload: Optional[Dict[str, Any]] = None,\n    token_owner: str = JWT_SALEOR_OWNER_NAME,\n) -> Dict[str, Any]:\n    payload = jwt_base_payload(exp_delta, token_owner)\n    payload.update(\n        {\n            \"token\": user.jwt_token_key,\n            \"email\": user.email,\n            \"type\": token_type,\n            \"user_id\": graphene.Node.to_global_id(\"User\", user.id),\n            \"is_staff\": user.is_staff,\n        }\n    )\n    if additional_payload:\n        payload.update(additional_payload)\n    return payload\n\n\ndef jwt_encode(payload: Dict[str, Any]) -> str:\n    jwt_manager = get_jwt_manager()\n    return jwt_manager.encode(payload)\n\n\ndef jwt_decode_with_exception_handler(\n    token: str, verify_expiration=settings.JWT_EXPIRE\n) -> Optional[Dict[str, Any]]:\n    try:\n        return jwt_decode(token, verify_expiration=verify_expiration)\n    except jwt.PyJWTError:\n        return None\n\n\ndef jwt_decode(\n    token: str, verify_expiration=settings.JWT_EXPIRE, verify_aud: bool = False\n) -> Dict[str, Any]:\n    jwt_manager = get_jwt_manager()\n    return jwt_manager.decode(token, verify_expiration, verify_aud=verify_aud)\n\n\ndef create_token(payload: Dict[str, Any], exp_delta: timedelta) -> str:\n    payload.update(jwt_base_payload(exp_delta, token_owner=JWT_SALEOR_OWNER_NAME))\n    return jwt_encode(payload)\n\n\ndef create_access_token(\n    user: User, additional_payload: Optional[Dict[str, Any]] = None\n) -> str:\n    payload = jwt_user_payload(\n        user, JWT_ACCESS_TYPE, settings.JWT_TTL_ACCESS, additional_payload\n    )\n    return jwt_encode(payload)\n\n\ndef create_refresh_token(\n    user: User, additional_payload: Optional[Dict[str, Any]] = None\n) -> str:\n    payload = jwt_user_payload(\n        user,\n        JWT_REFRESH_TYPE,\n        settings.JWT_TTL_REFRESH,\n        additional_payload,\n    )\n    return jwt_encode(payload)\n\n\ndef get_user_from_payload(payload: Dict[str, Any], request=None) -> Optional[User]:\n    # TODO: dataloader\n    user = User.objects.filter(email=payload[\"email\"], is_active=True).first()\n    user_jwt_token = payload.get(\"token\")\n    if not user_jwt_token or not user:\n        raise jwt.InvalidTokenError(\n            \"Invalid token. Create new one by using tokenCreate mutation.\"\n        )\n    if user.jwt_token_key != user_jwt_token:\n        raise jwt.InvalidTokenError(\n            \"Invalid token. Create new one by using tokenCreate mutation.\"\n        )\n    return user\n\n\ndef is_saleor_token(token: str) -> bool:\n    \"\"\"Confirm that token was generated by Saleor not by plugin.\"\"\"\n    try:\n        payload = jwt.decode(token, options={\"verify_signature\": False})\n    except jwt.PyJWTError:\n        return False\n    owner = payload.get(JWT_OWNER_FIELD)\n    if not owner or owner != JWT_SALEOR_OWNER_NAME:\n        return False\n    return True\n\n\ndef get_user_from_access_payload(payload: dict, request=None) -> Optional[User]:\n    jwt_type = payload.get(\"type\")\n    if jwt_type not in [JWT_ACCESS_TYPE, JWT_THIRDPARTY_ACCESS_TYPE]:\n        raise jwt.InvalidTokenError(\n            \"Invalid token. Create new one by using tokenCreate mutation.\"\n        )\n    permissions = payload.get(PERMISSIONS_FIELD, None)\n    user = get_user_from_payload(payload, request)\n    if user:\n        if permissions is not None:\n            token_permissions = get_permissions_from_names(permissions)\n            token_codenames = [perm.codename for perm in token_permissions]\n            user.effective_permissions = get_permissions_from_codenames(token_codenames)\n            user.is_staff = True if user.effective_permissions else False\n\n        if payload.get(\"is_staff\"):\n            user.is_staff = True\n    return user\n\n\ndef _create_access_token_for_third_party_actions(\n    permissions: Iterable[\"Permission\"],\n    user: \"User\",\n    app: \"App\",\n    extra: Optional[Dict[str, Any]] = None,\n):\n    app_permission_enums = get_permission_names(permissions)\n\n    permissions = user.effective_permissions\n    user_permission_enums = get_permission_names(permissions)\n    additional_payload = {\n        APP_KEY_FIELD: graphene.Node.to_global_id(\"App\", app.id),\n        PERMISSIONS_FIELD: list(app_permission_enums & user_permission_enums),\n        USER_PERMISSION_FIELD: list(user_permission_enums),\n    }\n    if app.audience:\n        additional_payload[\"aud\"] = app.audience\n    if extra:\n        additional_payload.update(extra)\n\n    payload = jwt_user_payload(\n        user,\n        JWT_THIRDPARTY_ACCESS_TYPE,\n        exp_delta=settings.JWT_TTL_APP_ACCESS,\n        additional_payload=additional_payload,\n    )\n    return jwt_encode(payload)\n\n\ndef create_access_token_for_app(app: \"App\", user: \"User\"):\n    \"\"\"Create access token for app.\n\n    App can use user's JWT token to proceed given operation in Saleor.\n    The token which can be used by App has additional field defining the permissions\n    assigned to it. The permissions set is the intersection of user permissions and\n    app permissions.\n    \"\"\"\n    app_permissions = app.permissions.all()\n    return _create_access_token_for_third_party_actions(\n        permissions=app_permissions, user=user, app=app\n    )\n\n\ndef create_access_token_for_app_extension(\n    app_extension: \"AppExtension\",\n    permissions: Iterable[\"Permission\"],\n    user: \"User\",\n    app: \"App\",\n):\n    app_extension_id = graphene.Node.to_global_id(\"AppExtension\", app_extension.id)\n    return _create_access_token_for_third_party_actions(\n        permissions=permissions,\n        user=user,\n        app=app,\n        extra={\"app_extension\": app_extension_id},\n    )\n", "path": "saleor/core/jwt.py"}]}