django-restit 4.2.93__py3-none-any.whl → 4.2.95__py3-none-any.whl
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.
- account/fcm/__init__.py +1 -33
- account/fcm/legacy.py +35 -0
- account/fcm/v1.py +97 -39
- account/migrations/0021_alter_cloudcredentials_group.py +19 -0
- account/models/device.py +21 -14
- account/rpc/device.py +1 -22
- {django_restit-4.2.93.dist-info → django_restit-4.2.95.dist-info}/METADATA +1 -1
- {django_restit-4.2.93.dist-info → django_restit-4.2.95.dist-info}/RECORD +11 -10
- wiki/models/page.py +1 -1
- account/fcm/google.py +0 -53
- {django_restit-4.2.93.dist-info → django_restit-4.2.95.dist-info}/LICENSE.md +0 -0
- {django_restit-4.2.93.dist-info → django_restit-4.2.95.dist-info}/WHEEL +0 -0
account/fcm/__init__.py
CHANGED
@@ -1,33 +1 @@
|
|
1
|
-
import
|
2
|
-
from rest import settings
|
3
|
-
from rest import log
|
4
|
-
|
5
|
-
# SEND_URL = "POST https://fcm.googleapis.com/v1/{parent=projects/*}/messages:send"
|
6
|
-
SEND_URL = "https://fcm.googleapis.com/fcm/send"
|
7
|
-
CM_PROVIDERS = settings.get("CM_PROVIDERS", None)
|
8
|
-
|
9
|
-
FCM_KEY = CM_PROVIDERS["fcm"]["key"]
|
10
|
-
|
11
|
-
logger = log.getLogger("fcm", filename="fcm.log")
|
12
|
-
|
13
|
-
|
14
|
-
def sendToDevice(device, data):
|
15
|
-
return sendData(device.cm_token, data)
|
16
|
-
|
17
|
-
|
18
|
-
def sendNotification(to_token, title, body):
|
19
|
-
return postMessage(dict(to=to_token, notification=dict(title=title, body=body)))
|
20
|
-
|
21
|
-
|
22
|
-
def sendData(to_token, data, priority="high"):
|
23
|
-
return postMessage(dict(to=to_token, data=data, content_available=True, priority=priority))
|
24
|
-
|
25
|
-
|
26
|
-
def postMessage(payload):
|
27
|
-
logger.info("sending FCM", payload)
|
28
|
-
headers = dict(Authorization=F"key={FCM_KEY}")
|
29
|
-
headers['Content-type'] = 'application/json'
|
30
|
-
resp = requests.post(SEND_URL, json=payload, headers=headers)
|
31
|
-
logger.info("response", resp.text)
|
32
|
-
return resp
|
33
|
-
|
1
|
+
from . import v1
|
account/fcm/legacy.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import requests
|
2
|
+
from rest import settings
|
3
|
+
from rest import log
|
4
|
+
|
5
|
+
|
6
|
+
SEND_URL = "https://fcm.googleapis.com/fcm/send"
|
7
|
+
CM_PROVIDERS = settings.get("CM_PROVIDERS", None)
|
8
|
+
|
9
|
+
FCM_KEY = None
|
10
|
+
if CM_PROVIDERS and "fcm" in CM_PROVIDERS:
|
11
|
+
FCM_KEY = CM_PROVIDERS["fcm"]["key"]
|
12
|
+
|
13
|
+
logger = log.getLogger("fcm", filename="fcm.log")
|
14
|
+
|
15
|
+
|
16
|
+
def sendToDevice(device, data):
|
17
|
+
return sendData(device.cm_token, data)
|
18
|
+
|
19
|
+
|
20
|
+
def sendNotification(to_token, title, body):
|
21
|
+
return postMessage(dict(to=to_token, notification=dict(title=title, body=body)))
|
22
|
+
|
23
|
+
|
24
|
+
def sendData(to_token, data, priority="high"):
|
25
|
+
return postMessage(dict(to=to_token, data=data, content_available=True, priority=priority))
|
26
|
+
|
27
|
+
|
28
|
+
def postMessage(payload):
|
29
|
+
logger.info("sending FCM", payload)
|
30
|
+
headers = dict(Authorization=F"key={FCM_KEY}")
|
31
|
+
headers['Content-type'] = 'application/json'
|
32
|
+
resp = requests.post(SEND_URL, json=payload, headers=headers)
|
33
|
+
logger.info("response", resp.text)
|
34
|
+
return resp
|
35
|
+
|
account/fcm/v1.py
CHANGED
@@ -1,47 +1,105 @@
|
|
1
|
-
|
2
|
-
import
|
1
|
+
import json
|
2
|
+
import time
|
3
|
+
import jwt # PyJWT library
|
3
4
|
import requests
|
4
|
-
from
|
5
|
-
from rest import log
|
5
|
+
from objict import objict
|
6
6
|
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def sendData(to_token, data, priority="high"):
|
33
|
-
return postMessage(dict(token=to_token, data=data, content_available=True, priority=priority))
|
34
|
-
|
8
|
+
def create_jwt(service_account_info):
|
9
|
+
"""
|
10
|
+
Create a JWT (JSON Web Token) for service account authentication.
|
11
|
+
|
12
|
+
:param service_account_info: Dictionary containing service account credentials.
|
13
|
+
:return: JWT string.
|
14
|
+
"""
|
15
|
+
now = int(time.time())
|
16
|
+
payload = {
|
17
|
+
'iss': service_account_info['client_email'],
|
18
|
+
'sub': service_account_info['client_email'],
|
19
|
+
'aud': 'https://oauth2.googleapis.com/token',
|
20
|
+
'iat': now,
|
21
|
+
'exp': now + 3600,
|
22
|
+
'scope': 'https://www.googleapis.com/auth/firebase.messaging'
|
23
|
+
}
|
24
|
+
|
25
|
+
additional_headers = {
|
26
|
+
'kid': service_account_info['private_key_id']
|
27
|
+
}
|
28
|
+
|
29
|
+
token = jwt.encode(payload, service_account_info['private_key'], algorithm='RS256', headers=additional_headers)
|
30
|
+
return token
|
35
31
|
|
36
|
-
def
|
37
|
-
|
32
|
+
def get_access_token(jwt_token):
|
33
|
+
"""
|
34
|
+
Exchange a JWT for an access token.
|
35
|
+
|
36
|
+
:param jwt_token: JWT string.
|
37
|
+
:return: Access token string.
|
38
|
+
"""
|
39
|
+
url = 'https://oauth2.googleapis.com/token'
|
38
40
|
headers = {
|
39
|
-
'
|
40
|
-
|
41
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
42
|
+
}
|
43
|
+
payload = {
|
44
|
+
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
45
|
+
'assertion': jwt_token
|
41
46
|
}
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
return
|
47
|
+
|
48
|
+
response = requests.post(url, headers=headers, data=payload)
|
49
|
+
response_data = response.json()
|
50
|
+
return response_data['access_token']
|
46
51
|
|
52
|
+
class FirebaseNotifier:
|
53
|
+
def __init__(self, service_account_info):
|
54
|
+
"""
|
55
|
+
Initialize the FirebaseNotifier with the project ID and service account info.
|
56
|
+
|
57
|
+
:param project_id: The project ID from Firebase project settings.
|
58
|
+
:param service_account_info: Dictionary containing service account credentials.
|
59
|
+
"""
|
60
|
+
self.project_id = service_account_info.get("project_id")
|
61
|
+
self.service_account_info = service_account_info
|
62
|
+
self.fcm_url = f'https://fcm.googleapis.com/v1/projects/{self.project_id}/messages:send'
|
63
|
+
self.jwt_token = None
|
64
|
+
self._jwt_expires = 0
|
65
|
+
self.access_token = None
|
47
66
|
|
67
|
+
@property
|
68
|
+
def is_jwt_expired(self):
|
69
|
+
return time.time() > self._jwt_expires
|
70
|
+
|
71
|
+
|
72
|
+
def _get_access_token(self):
|
73
|
+
"""
|
74
|
+
Get the access token, refreshing if necessary.
|
75
|
+
|
76
|
+
:return: Access token string.
|
77
|
+
"""
|
78
|
+
if not self.jwt_token or self.is_jwt_expired:
|
79
|
+
self.jwt_token = create_jwt(self.service_account_info)
|
80
|
+
self._jwt_expires = time.time() + 3000
|
81
|
+
self.access_token = get_access_token(self.jwt_token)
|
82
|
+
return self.access_token
|
83
|
+
|
84
|
+
def send(self, registration_token, title, body, data=None):
|
85
|
+
"""
|
86
|
+
Send a notification to the specified registration token.
|
87
|
+
|
88
|
+
:param registration_token: Device registration token.
|
89
|
+
:param title: Title of the notification.
|
90
|
+
:param body: Body of the notification.
|
91
|
+
:param data: Optional data payload to send along with the notification.
|
92
|
+
:return: Response from the FCM server.
|
93
|
+
"""
|
94
|
+
access_token = self._get_access_token()
|
95
|
+
headers = {
|
96
|
+
'Content-Type': 'application/json',
|
97
|
+
'Authorization': f'Bearer {access_token}',
|
98
|
+
}
|
99
|
+
message = dict(token=registration_token)
|
100
|
+
if title and body:
|
101
|
+
message["notification"] = dict(title=title, body=body)
|
102
|
+
if data:
|
103
|
+
message["data"] = data
|
104
|
+
payload = dict(message=message)
|
105
|
+
return requests.post(self.fcm_url, headers=headers, data=json.dumps(payload))
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Generated by Django 4.1.5 on 2024-06-07 20:05
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
dependencies = [
|
10
|
+
('account', '0020_cloudcredentials_cloudcredentialsmetadata'),
|
11
|
+
]
|
12
|
+
|
13
|
+
operations = [
|
14
|
+
migrations.AlterField(
|
15
|
+
model_name='cloudcredentials',
|
16
|
+
name='group',
|
17
|
+
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cloud_credentials', to='account.group'),
|
18
|
+
),
|
19
|
+
]
|
account/models/device.py
CHANGED
@@ -39,13 +39,13 @@ class MemberDevice(models.Model, rm.RestModel, rm.MetaDataModel):
|
|
39
39
|
}
|
40
40
|
|
41
41
|
def sendData(self, message, **kwargs):
|
42
|
-
messenger =
|
42
|
+
messenger = getCloudMessenger(self.cm_provider)
|
43
43
|
if messenger:
|
44
44
|
return messenger.sendToDevice(self, message)
|
45
45
|
return objict(status_code=404, reason=self.cm_provider)
|
46
46
|
|
47
47
|
def sendNotification(self, title, body):
|
48
|
-
messenger =
|
48
|
+
messenger = getCloudMessenger(self.cm_provider)
|
49
49
|
if messenger:
|
50
50
|
return messenger.sendNotification(self.cm_token, title, body)
|
51
51
|
return objict(status_code=404, reason=self.cm_provider)
|
@@ -129,7 +129,9 @@ class CloudCredentials(models.Model, rm.RestModel, rm.MetaDataModel):
|
|
129
129
|
|
130
130
|
created = models.DateTimeField(auto_now_add=True)
|
131
131
|
modified = models.DateTimeField(auto_now=True)
|
132
|
-
group = models.ForeignKey(
|
132
|
+
group = models.ForeignKey(
|
133
|
+
"account.Group", null=True, default=None,
|
134
|
+
related_name="cloud_credentials", on_delete=models.CASCADE)
|
133
135
|
|
134
136
|
name = models.CharField(max_length=128, blank=True, null=True, default=None)
|
135
137
|
uuid = models.CharField(db_index=True, max_length=64, blank=True, null=True, default=None)
|
@@ -137,23 +139,28 @@ class CloudCredentials(models.Model, rm.RestModel, rm.MetaDataModel):
|
|
137
139
|
|
138
140
|
credentials = rf.JSONField()
|
139
141
|
|
142
|
+
_notifier = None
|
143
|
+
|
144
|
+
@property
|
145
|
+
def notifier(self):
|
146
|
+
if self._notifier is None:
|
147
|
+
from account import fcm
|
148
|
+
self._notifier = fcm.v1.FirebaseNotifier(self.credentials)
|
149
|
+
return self._notifier
|
150
|
+
|
140
151
|
def sendToDevice(self, device, message):
|
141
|
-
|
152
|
+
self.notifier.send(device.cm_token, None, None, message)
|
142
153
|
|
143
154
|
def sendNotification(self, token, title, body):
|
144
|
-
|
155
|
+
self.notifier.send(token, title, body)
|
145
156
|
|
146
157
|
|
147
158
|
class CloudCredentialsMetaData(rm.MetaDataBase):
|
148
159
|
parent = models.ForeignKey(CloudCredentials, related_name="properties", on_delete=models.CASCADE)
|
149
160
|
|
150
161
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
if name == "fcm":
|
157
|
-
from account import fcm
|
158
|
-
CM_BACKENDS["fcm"] = fcm
|
159
|
-
return CM_BACKENDS.get(name, None)
|
162
|
+
def getCloudMessenger(name):
|
163
|
+
creds = CloudCredentials.objects.filter(uuid=name).last()
|
164
|
+
if creds is not None:
|
165
|
+
return creds
|
166
|
+
return None
|
account/rpc/device.py
CHANGED
@@ -19,33 +19,12 @@ def rest_on_member_device(request, pk=None):
|
|
19
19
|
return am.MemberDevice.on_rest_request(request, pk)
|
20
20
|
|
21
21
|
|
22
|
-
@rd.urlPOST(
|
22
|
+
@rd.urlPOST('member/device/register')
|
23
23
|
@rd.login_required
|
24
24
|
def rest_on_member_device_register(request):
|
25
25
|
# this requires a JWT with a device_id
|
26
26
|
if not request.device_id:
|
27
27
|
return rv.restPermissionDenied(request, "requires jwt with device id")
|
28
|
-
cm_token = request.DATA.get("cm_token")
|
29
|
-
device_id = request.device_id
|
30
|
-
if not device_id:
|
31
|
-
return rv.restStatus(request, False, error="missing device id")
|
32
|
-
md = am.MemberDevice.objects.filter(uuid=device_id).last()
|
33
|
-
if md is None:
|
34
|
-
md = am.MemberDevice.createFromRequest(request, uuid=device_id, user=request.member)
|
35
|
-
elif cm_token is not None and md.cm_token != cm_token:
|
36
|
-
md.cm_token = cm_token
|
37
|
-
md.cm_provider = "fcm"
|
38
|
-
md.save()
|
39
|
-
return rv.restStatus(request, True)
|
40
|
-
|
41
|
-
|
42
|
-
@rd.urlPOST(r'^member/device/register$')
|
43
|
-
@rd.login_required
|
44
|
-
def rest_on_member_device_register(request):
|
45
|
-
# this requires a JWT with a device_id
|
46
|
-
if not request.device_id:
|
47
|
-
return rv.restPermissionDenied(request, "requires jwt with device id")
|
48
|
-
|
49
28
|
md = am.MemberDevice.register(request, request.member, request.device_id)
|
50
29
|
return rv.restStatus(request, md is not None)
|
51
30
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
account/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
account/admin.py,sha256=8MQ1gAgjpPUC_SGCVMwd7I8fqLetqcutLiasjssEPRY,1839
|
3
|
-
account/fcm/__init__.py,sha256=
|
4
|
-
account/fcm/
|
5
|
-
account/fcm/v1.py,sha256=
|
3
|
+
account/fcm/__init__.py,sha256=lXuz5z0bL5TXqVdaXxiVr-4zF_Q1IZ8kPEkuYGb5eB0,17
|
4
|
+
account/fcm/legacy.py,sha256=5WM5HAnjVPweT0oe3rBN9tBGHfC-DPa-e9luH3baT8Y,959
|
5
|
+
account/fcm/v1.py,sha256=dMCHvDRgTrkkrfFocxuVvBo7sAzqonOO1IqVYp2B7Jk,3601
|
6
6
|
account/migrations/0001_initial.py,sha256=PhYNDTiwjyUplErBmYc34ecynLIEJL2JuC02o8GCXes,15894
|
7
7
|
account/migrations/0003_member_phone_number.py,sha256=auAJCfxsK-y3Veo0vwZfrIZxvwHYBjg5CpYRgCWghCQ,738
|
8
8
|
account/migrations/0004_group_modified_alter_group_created.py,sha256=20iNFlUGtRCWUsxEqTJFRQKQ73z4REhfo-hGam8IHXY,551
|
@@ -22,9 +22,10 @@ account/migrations/0017_rename_requires_topt_member_requires_totp.py,sha256=GksG
|
|
22
22
|
account/migrations/0018_userpasskey.py,sha256=SdXYo4TkIeP5wLNfCza3Jq5-gKuUufzTHGBw0hFQOMY,1475
|
23
23
|
account/migrations/0019_group_location.py,sha256=EfMB_w4qWUGDqQeNc453PFZwpjpTeoA6xr6Qgo_YAOM,601
|
24
24
|
account/migrations/0020_cloudcredentials_cloudcredentialsmetadata.py,sha256=mHwxkyDfA4ueQOt34w5ndJB4XwNTDLv79CkKgzhlz-c,2250
|
25
|
+
account/migrations/0021_alter_cloudcredentials_group.py,sha256=zoFYmE-hd3uRGX6DRO9k-osPwH0jFeTU7S-pjCOtakk,561
|
25
26
|
account/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
27
|
account/models/__init__.py,sha256=cV_lMnT2vL_mjiYtT4hlcIHo52ocFbGSNVkOIHHLXZY,385
|
27
|
-
account/models/device.py,sha256=
|
28
|
+
account/models/device.py,sha256=dKq6fDQSKkeBrq9glZtmewSQPDBCnYuUlBEx7ZonfBc,5693
|
28
29
|
account/models/feeds.py,sha256=vI7fG4ASY1M0Zjke24RdnfDcuWeATl_yR_25jPmT64g,2011
|
29
30
|
account/models/group.py,sha256=mjWwePt3ogQUo9m0EhURMz0aBrVVx_0Drr0lNDESQio,22281
|
30
31
|
account/models/legacy.py,sha256=zYdtv4LC0ooxPVqWM-uToPwV-lYWQLorSE6p6yn1xDw,2720
|
@@ -41,7 +42,7 @@ account/passkeys/core.py,sha256=xj-vXjSrfWDvc5MYtEmXzwaMkNHl-cXrQKVrN9soRCg,4126
|
|
41
42
|
account/periodic.py,sha256=-u0n-7QTJgDOkasGhBAPwHAwjpqWGA-MZLEFkVTqCGU,874
|
42
43
|
account/rpc/__init__.py,sha256=SGF0M_-H0dKh3b1apSX29BotNWAvITYccGQVC0MIjL8,336
|
43
44
|
account/rpc/auth.py,sha256=p62tyihWvSY2zn7eaVxKcCG9qLnnZBIkwdRH81sS5_Y,16615
|
44
|
-
account/rpc/device.py,sha256=
|
45
|
+
account/rpc/device.py,sha256=lU2BHNPreHV0dDTjAPc7Sc-5m2JP8SiWVqiKuBfV7Fo,2281
|
45
46
|
account/rpc/group.py,sha256=riymUK6ttv8G6SG6K-v7UCvM40WQy3HaGSeF_sEiaU0,4929
|
46
47
|
account/rpc/member.py,sha256=VNRSD38mmTUCbq3cCSy3qRyquzeVEwW_8zzrtS0-vAA,1817
|
47
48
|
account/rpc/notify.py,sha256=Q2YWejP36egeF060Hih5uX4Psv_B8NWlLLPi7iDYlIw,3344
|
@@ -479,7 +480,7 @@ wiki/migrations/0002_alter_pagemedia_entry.py,sha256=9CUnfvBmj0D4akCkux7HFuXgw9B
|
|
479
480
|
wiki/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
480
481
|
wiki/models/__init__.py,sha256=jE-9r_Hqpyo7ysKu9BschXOn5Zg34wUt894GwJpxA28,132
|
481
482
|
wiki/models/faq.py,sha256=nvcEFerllQKT61kIYlasvZzRKwpXyfmQpiqkpHP1V1o,1745
|
482
|
-
wiki/models/page.py,sha256=
|
483
|
+
wiki/models/page.py,sha256=7_HAbvHtWIQpttiMoxrm8-Mfik6kUW6F47xkXZN2Omk,7311
|
483
484
|
wiki/models/revision.py,sha256=St5-vz8SGvogsDL6jTWqHLE23PS5mp9iA0DUt3hWTsU,729
|
484
485
|
wiki/periodic.py,sha256=t-UgXJIug-OLslJM_r03-5WrNKj39TxrvfuNFjVAhDs,334
|
485
486
|
wiki/renderers/__init__.py,sha256=lLEoJvjU3ezXwBGcjleKk_kMyNeMD9MpfBlEiKayEiM,461
|
@@ -506,7 +507,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
|
|
506
507
|
ws4redis/settings.py,sha256=K0yBiLUuY81iDM4Yr-k8hbvjn5VVHu5zQhmMK8Dtz0s,1536
|
507
508
|
ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
|
508
509
|
ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
|
509
|
-
django_restit-4.2.
|
510
|
-
django_restit-4.2.
|
511
|
-
django_restit-4.2.
|
512
|
-
django_restit-4.2.
|
510
|
+
django_restit-4.2.95.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
|
511
|
+
django_restit-4.2.95.dist-info/METADATA,sha256=K21UAXzCkyfR09FSmBEKjfKpusJtmjN5JxSqTyXeoCc,7662
|
512
|
+
django_restit-4.2.95.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
513
|
+
django_restit-4.2.95.dist-info/RECORD,,
|
wiki/models/page.py
CHANGED
account/fcm/google.py
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import requests
|
3
|
-
import time
|
4
|
-
from jwt import JWT, jwk_from_dict
|
5
|
-
from datetime import datetime, timedelta
|
6
|
-
|
7
|
-
|
8
|
-
def oauthLogin(service_account_info):
|
9
|
-
# Token endpoint
|
10
|
-
token_uri = service_account_info["token_uri"]
|
11
|
-
|
12
|
-
# The current time and expiration time for the assertion
|
13
|
-
issued_at_time = datetime.utcnow()
|
14
|
-
expiration_time = issued_at_time + timedelta(minutes=60)
|
15
|
-
|
16
|
-
# JWT Header
|
17
|
-
jwt_header = {
|
18
|
-
"alg": "RS256",
|
19
|
-
"typ": "JWT",
|
20
|
-
"kid": service_account_info["private_key_id"]
|
21
|
-
}
|
22
|
-
|
23
|
-
# JWT Payload
|
24
|
-
jwt_payload = {
|
25
|
-
"iss": service_account_info["client_email"],
|
26
|
-
"sub": service_account_info["client_email"],
|
27
|
-
"aud": token_uri,
|
28
|
-
"iat": int(issued_at_time.timestamp()),
|
29
|
-
"exp": int(expiration_time.timestamp()),
|
30
|
-
"scope": "https://www.googleapis.com/auth/firebase.messaging"
|
31
|
-
}
|
32
|
-
|
33
|
-
# Create a JWT
|
34
|
-
jwt_instance = JWT()
|
35
|
-
private_key = jwk_from_dict({"k": service_account_info["private_key"], "kty": "RSA"})
|
36
|
-
assertion = jwt_instance.encode(jwt_header, jwt_payload, private_key)
|
37
|
-
|
38
|
-
# Exchange the JWT for an access token
|
39
|
-
response = requests.post(token_uri, data={
|
40
|
-
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
41
|
-
"assertion": assertion
|
42
|
-
})
|
43
|
-
|
44
|
-
response_data = response.json()
|
45
|
-
|
46
|
-
access_token = response_data.get("access_token")
|
47
|
-
expires_in = response_data.get("expires_in") # Seconds until the token expires
|
48
|
-
|
49
|
-
# You can now use this access_token to authenticate requests to Google APIs.
|
50
|
-
# Remember to refresh the token using a similar process once it's close to expiration.
|
51
|
-
|
52
|
-
print("Access Token:", access_token)
|
53
|
-
print("Expires In:", expires_in)
|
File without changes
|
File without changes
|