pyios-firebase 0.2.0__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.
- pyios_firebase/__init__.py +26 -0
- pyios_firebase/analytics/__init__.py +434 -0
- pyios_firebase/analytics/names.py +251 -0
- pyios_firebase/app_check/__init__.py +483 -0
- pyios_firebase/auth/__init__.py +131 -0
- pyios_firebase/auth/_apple_name_cache.py +97 -0
- pyios_firebase/auth/_common.py +121 -0
- pyios_firebase/auth/_ui.py +21 -0
- pyios_firebase/auth/apple.py +326 -0
- pyios_firebase/auth/auth.py +592 -0
- pyios_firebase/auth/credentials.py +551 -0
- pyios_firebase/auth/errors.py +321 -0
- pyios_firebase/auth/facebook.py +240 -0
- pyios_firebase/auth/google.py +167 -0
- pyios_firebase/auth/multi_factor.py +394 -0
- pyios_firebase/auth/results.py +361 -0
- pyios_firebase/auth/user.py +457 -0
- pyios_firebase/core/__init__.py +32 -0
- pyios_firebase/crashlytics/__init__.py +463 -0
- pyios_firebase/database/__init__.py +62 -0
- pyios_firebase/database/database.py +144 -0
- pyios_firebase/database/errors.py +80 -0
- pyios_firebase/database/query.py +569 -0
- pyios_firebase/database/reference.py +428 -0
- pyios_firebase/database/snapshot.py +218 -0
- pyios_firebase/database/values.py +271 -0
- pyios_firebase/firestore/__init__.py +59 -0
- pyios_firebase/firestore/_limits.py +48 -0
- pyios_firebase/firestore/_query_state.py +228 -0
- pyios_firebase/firestore/_write.py +18 -0
- pyios_firebase/firestore/aggregate.py +137 -0
- pyios_firebase/firestore/collection_reference.py +100 -0
- pyios_firebase/firestore/document_change.py +63 -0
- pyios_firebase/firestore/document_reference.py +229 -0
- pyios_firebase/firestore/document_snapshot.py +102 -0
- pyios_firebase/firestore/enums.py +66 -0
- pyios_firebase/firestore/field_value.py +94 -0
- pyios_firebase/firestore/filter.py +319 -0
- pyios_firebase/firestore/firestore.py +184 -0
- pyios_firebase/firestore/listener_registration.py +55 -0
- pyios_firebase/firestore/query.py +679 -0
- pyios_firebase/firestore/query_snapshot.py +103 -0
- pyios_firebase/firestore/snapshot_metadata.py +36 -0
- pyios_firebase/firestore/transaction.py +130 -0
- pyios_firebase/firestore/write_batch.py +125 -0
- pyios_firebase/functions/__init__.py +329 -0
- pyios_firebase/messaging/__init__.py +37 -0
- pyios_firebase/messaging/messaging.py +435 -0
- pyios_firebase/messaging/notifications.py +292 -0
- pyios_firebase/performance/__init__.py +486 -0
- pyios_firebase/py.typed +0 -0
- pyios_firebase/remote_config/__init__.py +660 -0
- pyios_firebase/storage/__init__.py +41 -0
- pyios_firebase/storage/errors.py +103 -0
- pyios_firebase/storage/metadata.py +202 -0
- pyios_firebase/storage/reference.py +402 -0
- pyios_firebase/storage/storage.py +120 -0
- pyios_firebase/storage/task.py +198 -0
- pyios_firebase/types.py +93 -0
- pyios_firebase/util.py +413 -0
- pyios_firebase-0.2.0.dist-info/METADATA +1969 -0
- pyios_firebase-0.2.0.dist-info/RECORD +65 -0
- pyios_firebase-0.2.0.dist-info/WHEEL +5 -0
- pyios_firebase-0.2.0.dist-info/licenses/LICENSE +21 -0
- pyios_firebase-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"""Firebase App Check: prove requests come from your real app.
|
|
2
|
+
|
|
3
|
+
Choose a provider **before** ``core.configure()``::
|
|
4
|
+
|
|
5
|
+
from pyios_firebase import app_check, core
|
|
6
|
+
|
|
7
|
+
if app_check.running_in_simulator():
|
|
8
|
+
app_check.use_debug_provider() # register the debug token in the console
|
|
9
|
+
else:
|
|
10
|
+
app_check.use_app_attest_provider() # or use_device_check_provider()
|
|
11
|
+
core.configure()
|
|
12
|
+
|
|
13
|
+
Firestore, Functions, Storage, Auth and the other Firebase SDKs then attach App
|
|
14
|
+
Check tokens by themselves. For your own backend, get a token with
|
|
15
|
+
``AppCheck.app_check().token(...)``.
|
|
16
|
+
|
|
17
|
+
Needs the ``FirebaseAppCheck`` framework. App Attest also needs the App Attest
|
|
18
|
+
capability (``com.apple.developer.devicecheck.appattest-environment``).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
__all__ = (
|
|
24
|
+
"APP_CHECK_ERROR_DOMAIN",
|
|
25
|
+
"DEBUG_TOKEN_ENVIRONMENT_VARIABLE",
|
|
26
|
+
"AppCheck",
|
|
27
|
+
"AppCheckErrorCode",
|
|
28
|
+
"AppCheckProvider",
|
|
29
|
+
"AppCheckToken",
|
|
30
|
+
"AppCheckTokenListener",
|
|
31
|
+
"TokenCallback",
|
|
32
|
+
"initialize_debug_appcheck",
|
|
33
|
+
"running_in_simulator",
|
|
34
|
+
"use_app_attest_provider",
|
|
35
|
+
"use_custom_provider",
|
|
36
|
+
"use_debug_provider",
|
|
37
|
+
"use_device_check_provider",
|
|
38
|
+
"use_recaptcha_provider",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
import os
|
|
42
|
+
from abc import ABC, abstractmethod
|
|
43
|
+
from collections.abc import Callable
|
|
44
|
+
from dataclasses import dataclass
|
|
45
|
+
from datetime import UTC, datetime
|
|
46
|
+
from typing import Any, ClassVar, Final, cast
|
|
47
|
+
|
|
48
|
+
from rubicon.objc import NSObject, objc_method
|
|
49
|
+
from rubicon.objc.runtime import objc_block, objc_id
|
|
50
|
+
|
|
51
|
+
from ..types import FirebaseError, ObjCObject
|
|
52
|
+
from ..util import (
|
|
53
|
+
error_from_pointer,
|
|
54
|
+
listener_block,
|
|
55
|
+
nsdate_to_datetime,
|
|
56
|
+
objc_class,
|
|
57
|
+
objc_instance,
|
|
58
|
+
objc_protocols,
|
|
59
|
+
one_shot_block,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
APP_CHECK_ERROR_DOMAIN: Final = "com.firebase.appCheck"
|
|
63
|
+
"""The ``error.domain`` of App Check errors."""
|
|
64
|
+
DEBUG_TOKEN_ENVIRONMENT_VARIABLE: Final = "FIRAAppCheckDebugToken"
|
|
65
|
+
"""Set it (for example in CI) to make the debug provider use that token
|
|
66
|
+
instead of a random one. ``AppCheckDebugToken`` also works."""
|
|
67
|
+
|
|
68
|
+
_TOKEN_CHANGED: Final = "FIRAppCheckAppCheckTokenDidChangeNotification"
|
|
69
|
+
_TOKEN_KEY: Final = "FIRAppCheckTokenNotificationKey"
|
|
70
|
+
_PROVIDER_ERROR_DOMAIN: Final = "PyiOSFirebaseAppCheckProvider"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AppCheckErrorCode:
|
|
74
|
+
"""The ``error.code`` values of App Check errors (``FIRAppCheckErrorCode``)."""
|
|
75
|
+
|
|
76
|
+
UNKNOWN: Final = 0
|
|
77
|
+
SERVER_UNREACHABLE: Final = 1
|
|
78
|
+
"""No network, or the App Check server did not answer."""
|
|
79
|
+
INVALID_CONFIGURATION: Final = 2
|
|
80
|
+
"""The provider or the Firebase project is not set up right."""
|
|
81
|
+
KEYCHAIN: Final = 3
|
|
82
|
+
"""The keychain could not store the token."""
|
|
83
|
+
UNSUPPORTED: Final = 4
|
|
84
|
+
"""The provider does not work here (for example App Attest in the Simulator)."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class AppCheckToken:
|
|
89
|
+
"""An App Check token."""
|
|
90
|
+
|
|
91
|
+
token: str
|
|
92
|
+
"""The token: send it in the ``X-Firebase-AppCheck`` header to your backend."""
|
|
93
|
+
expiration_date: datetime
|
|
94
|
+
"""When it expires (UTC)."""
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_native(cls, native: ObjCObject) -> AppCheckToken:
|
|
98
|
+
"""Copy a native ``FIRAppCheckToken``.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
native: the ``FIRAppCheckToken``.
|
|
102
|
+
"""
|
|
103
|
+
expiration = nsdate_to_datetime(native.expirationDate)
|
|
104
|
+
return cls(str(native.token), expiration or datetime.fromtimestamp(0, UTC))
|
|
105
|
+
|
|
106
|
+
def to_native(self) -> ObjCObject:
|
|
107
|
+
"""Make the native ``FIRAppCheckToken``."""
|
|
108
|
+
date = objc_class("NSDate").dateWithTimeIntervalSince1970_(
|
|
109
|
+
self.expiration_date.timestamp()
|
|
110
|
+
)
|
|
111
|
+
return (
|
|
112
|
+
objc_class("FIRAppCheckToken")
|
|
113
|
+
.alloc()
|
|
114
|
+
.initWithToken_expirationDate_(self.token, date)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def running_in_simulator() -> bool:
|
|
119
|
+
"""True in the iOS Simulator, where App Attest and DeviceCheck do not work
|
|
120
|
+
(use the debug provider there)."""
|
|
121
|
+
return "SIMULATOR_DEVICE_NAME" in os.environ or "SIMULATOR_UDID" in os.environ
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _before_configure() -> None:
|
|
125
|
+
if objc_class("FIRApp").defaultApp() is not None:
|
|
126
|
+
raise RuntimeError(
|
|
127
|
+
"set the App Check provider before core.configure(): "
|
|
128
|
+
"Firebase reads it once, when the app is configured"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _set_factory(factory: ObjCObject) -> None:
|
|
133
|
+
_before_configure()
|
|
134
|
+
_factory_holder["factory"] = factory # Firebase keeps it too; this makes sure
|
|
135
|
+
objc_class("FIRAppCheck").setAppCheckProviderFactory_(factory)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_factory_holder: dict[str, ObjCObject] = {}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def use_debug_provider() -> None:
|
|
142
|
+
"""Use the debug provider: for the Simulator, development and CI.
|
|
143
|
+
|
|
144
|
+
On first run it prints ``Firebase App Check debug token: '...'`` in the
|
|
145
|
+
Xcode console; add that token in the Firebase console (App Check, your app,
|
|
146
|
+
Manage debug tokens). In CI, set ``DEBUG_TOKEN_ENVIRONMENT_VARIABLE`` to a
|
|
147
|
+
registered token. Never ship it: anyone with the token passes App Check.
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
RuntimeError: ``core.configure()`` was already called.
|
|
151
|
+
"""
|
|
152
|
+
_set_factory(objc_class("FIRAppCheckDebugProviderFactory").alloc().init())
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def initialize_debug_appcheck() -> None:
|
|
156
|
+
"""The 0.1 name of ``use_debug_provider``."""
|
|
157
|
+
use_debug_provider()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def use_device_check_provider() -> None:
|
|
161
|
+
"""Use Apple's DeviceCheck (the default provider). Works on real devices
|
|
162
|
+
with iOS 11+; not in the Simulator.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
RuntimeError: ``core.configure()`` was already called.
|
|
166
|
+
"""
|
|
167
|
+
_set_factory(objc_class("FIRDeviceCheckProviderFactory").alloc().init())
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def use_app_attest_provider() -> None:
|
|
171
|
+
"""Use Apple's App Attest: the strongest attestation, on real devices with
|
|
172
|
+
iOS 14+. Needs the App Attest capability; not in the Simulator.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
RuntimeError: ``core.configure()`` was already called.
|
|
176
|
+
"""
|
|
177
|
+
_set_factory(objc_class("FIRAppAttestProviderFactory").alloc().init())
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def use_recaptcha_provider(site_key: str) -> None:
|
|
181
|
+
"""Use reCAPTCHA Enterprise. Needs the reCAPTCHA Enterprise SDK in the app.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
site_key: the reCAPTCHA Enterprise site key for iOS.
|
|
185
|
+
|
|
186
|
+
Raises:
|
|
187
|
+
RuntimeError: ``core.configure()`` was already called.
|
|
188
|
+
"""
|
|
189
|
+
_set_factory(
|
|
190
|
+
objc_class("FIRRecaptchaProviderFactory").alloc().initWithSiteKey_(site_key)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ---- custom providers, written in Python -----------------------------------------
|
|
195
|
+
|
|
196
|
+
TokenCallback = Callable[[AppCheckToken | None, str | None], None]
|
|
197
|
+
"""``(token, None)`` or ``(None, error_message)``."""
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class AppCheckProvider(ABC):
|
|
201
|
+
"""A custom App Check provider, written in Python: for example tokens from
|
|
202
|
+
your own attestation service. Pass it to ``use_custom_provider``.
|
|
203
|
+
|
|
204
|
+
::
|
|
205
|
+
|
|
206
|
+
class MyProvider(AppCheckProvider):
|
|
207
|
+
def get_token(self, callback):
|
|
208
|
+
my_service.fetch(lambda token, expires: callback(AppCheckToken(token, expires), None))
|
|
209
|
+
|
|
210
|
+
Firebase may call these methods on a background thread.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
@abstractmethod
|
|
214
|
+
def get_token(self, callback: TokenCallback) -> None:
|
|
215
|
+
"""Make a token, then call ``callback`` exactly once.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
callback: call it with ``(AppCheckToken, None)``, or
|
|
219
|
+
``(None, "what went wrong")``.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
def get_limited_use_token(self, callback: TokenCallback) -> None:
|
|
223
|
+
"""Make a single-use token (for replay protection). By default, the
|
|
224
|
+
same as ``get_token``.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
callback: call it with ``(AppCheckToken, None)``, or
|
|
228
|
+
``(None, "what went wrong")``.
|
|
229
|
+
"""
|
|
230
|
+
self.get_token(callback)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _answer(handler: Any) -> TokenCallback:
|
|
234
|
+
"""A Python callback that answers Firebase's completion block once."""
|
|
235
|
+
state = {"done": False}
|
|
236
|
+
|
|
237
|
+
def callback(token: AppCheckToken | None, error: str | None) -> None:
|
|
238
|
+
if state["done"]:
|
|
239
|
+
return
|
|
240
|
+
state["done"] = True
|
|
241
|
+
if token is not None:
|
|
242
|
+
handler(token.to_native(), None)
|
|
243
|
+
return
|
|
244
|
+
nserror = objc_class("NSError").errorWithDomain_code_userInfo_(
|
|
245
|
+
_PROVIDER_ERROR_DOMAIN,
|
|
246
|
+
AppCheckErrorCode.UNKNOWN,
|
|
247
|
+
{"NSLocalizedDescription": error or "the App Check provider gave no token"},
|
|
248
|
+
)
|
|
249
|
+
handler(None, nserror)
|
|
250
|
+
|
|
251
|
+
return callback
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# the Python providers, by the address of their Objective-C bridge
|
|
255
|
+
_providers: dict[int, AppCheckProvider] = {}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _key(obj: ObjCObject) -> int:
|
|
259
|
+
return int(obj.ptr.value or 0)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _bridge_call(bridge: ObjCObject, method: str, handler: Any) -> None:
|
|
263
|
+
"""Run the Python provider's ``method`` for a call Firebase made on ``bridge``."""
|
|
264
|
+
callback = _answer(handler)
|
|
265
|
+
provider = _providers.get(_key(bridge))
|
|
266
|
+
if provider is None:
|
|
267
|
+
callback(None, "the App Check provider is gone")
|
|
268
|
+
return
|
|
269
|
+
try:
|
|
270
|
+
getattr(provider, method)(callback)
|
|
271
|
+
except Exception as e: # noqa: BLE001 - Firebase must always get an answer
|
|
272
|
+
callback(None, f"{type(e).__name__}: {e}")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class PyAppCheckProviderBridge( # type: ignore[call-arg] # Rubicon's class keyword
|
|
276
|
+
NSObject, protocols=objc_protocols("FIRAppCheckProvider")
|
|
277
|
+
):
|
|
278
|
+
"""Lets Firebase call a Python ``AppCheckProvider``.
|
|
279
|
+
|
|
280
|
+
Rubicon reads these methods' annotations to build their Objective-C
|
|
281
|
+
signatures: parameters must be annotated with ctypes types.
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
@objc_method
|
|
285
|
+
def getTokenWithCompletion_(self, handler: objc_block) -> None:
|
|
286
|
+
"""Firebase asks for a token."""
|
|
287
|
+
_bridge_call(self, "get_token", handler)
|
|
288
|
+
|
|
289
|
+
@objc_method
|
|
290
|
+
def getLimitedUseTokenWithCompletion_(self, handler: objc_block) -> None:
|
|
291
|
+
"""Firebase asks for a limited-use token."""
|
|
292
|
+
_bridge_call(self, "get_limited_use_token", handler)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class PyAppCheckProviderFactory( # type: ignore[call-arg] # Rubicon's class keyword
|
|
296
|
+
NSObject, protocols=objc_protocols("FIRAppCheckProviderFactory")
|
|
297
|
+
):
|
|
298
|
+
"""Lets Firebase ask Python for a provider for each Firebase app."""
|
|
299
|
+
|
|
300
|
+
@objc_method
|
|
301
|
+
def createProviderWithApp_(self, app: objc_id) -> objc_id:
|
|
302
|
+
"""Firebase asks for the provider of ``app``."""
|
|
303
|
+
make = _factories.get(_key(self))
|
|
304
|
+
if make is None:
|
|
305
|
+
return cast("objc_id", None)
|
|
306
|
+
bridge = PyAppCheckProviderBridge.alloc().init()
|
|
307
|
+
_providers[_key(bridge)] = make(objc_instance(app))
|
|
308
|
+
_bridges.append(bridge) # Firebase holds it; keep the Python side alive too
|
|
309
|
+
return cast("objc_id", bridge)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
_factories: dict[int, Callable[[ObjCObject], AppCheckProvider]] = {}
|
|
313
|
+
_bridges: list[ObjCObject] = []
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def use_custom_provider(
|
|
317
|
+
provider: AppCheckProvider | Callable[[ObjCObject], AppCheckProvider],
|
|
318
|
+
) -> None:
|
|
319
|
+
"""Use a custom provider written in Python.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
provider: an ``AppCheckProvider`` for every Firebase app, or a function
|
|
323
|
+
that gets the native ``FIRApp`` and returns the provider for it.
|
|
324
|
+
|
|
325
|
+
Raises:
|
|
326
|
+
RuntimeError: ``core.configure()`` was already called.
|
|
327
|
+
"""
|
|
328
|
+
factory = PyAppCheckProviderFactory.alloc().init()
|
|
329
|
+
if isinstance(provider, AppCheckProvider):
|
|
330
|
+
instance = provider
|
|
331
|
+
_factories[_key(factory)] = lambda _app: instance
|
|
332
|
+
else:
|
|
333
|
+
_factories[_key(factory)] = provider
|
|
334
|
+
_set_factory(factory)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# ---- tokens ---------------------------------------------------------------------
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class AppCheckTokenListener:
|
|
341
|
+
"""Given back by ``AppCheck.add_token_listener``. Call ``remove()`` to stop.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
observer: the native ``NSNotificationCenter`` observer.
|
|
345
|
+
release: releases the listener's callback block.
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
def __init__(self, observer: ObjCObject, release: Callable[[], None]) -> None:
|
|
349
|
+
self._observer: ObjCObject | None = observer
|
|
350
|
+
self._release = release
|
|
351
|
+
|
|
352
|
+
def remove(self) -> None:
|
|
353
|
+
"""Stop the updates. Safe to call more than once."""
|
|
354
|
+
if self._observer is None:
|
|
355
|
+
return
|
|
356
|
+
objc_class("NSNotificationCenter").defaultCenter.removeObserver_(self._observer)
|
|
357
|
+
self._observer = None
|
|
358
|
+
self._release()
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
class AppCheck:
|
|
362
|
+
"""App Check for the default Firebase app. Get it with ``AppCheck.app_check()``.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
native: the native ``FIRAppCheck``.
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
_instance: ClassVar[AppCheck | None] = None
|
|
369
|
+
|
|
370
|
+
def __init__(self, native: ObjCObject) -> None:
|
|
371
|
+
self._native = native
|
|
372
|
+
|
|
373
|
+
@classmethod
|
|
374
|
+
def app_check(cls) -> AppCheck:
|
|
375
|
+
"""The shared instance (after ``core.configure()``)."""
|
|
376
|
+
if cls._instance is None:
|
|
377
|
+
cls._instance = cls(objc_class("FIRAppCheck").appCheck())
|
|
378
|
+
return cls._instance
|
|
379
|
+
|
|
380
|
+
@property
|
|
381
|
+
def native(self) -> ObjCObject:
|
|
382
|
+
"""The ``FIRAppCheck``."""
|
|
383
|
+
return self._native
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def is_token_auto_refresh_enabled(self) -> bool:
|
|
387
|
+
"""Whether App Check refreshes the token before it expires (the default
|
|
388
|
+
comes from ``FirebaseAppCheckTokenAutoRefreshEnabled`` in Info.plist,
|
|
389
|
+
else Firebase's data collection setting)."""
|
|
390
|
+
value = self._native.isTokenAutoRefreshEnabled
|
|
391
|
+
return bool(value() if callable(value) else value)
|
|
392
|
+
|
|
393
|
+
@is_token_auto_refresh_enabled.setter
|
|
394
|
+
def is_token_auto_refresh_enabled(self, enabled: bool) -> None:
|
|
395
|
+
self._native.isTokenAutoRefreshEnabled = bool(enabled)
|
|
396
|
+
|
|
397
|
+
def token(
|
|
398
|
+
self,
|
|
399
|
+
callback: Callable[[AppCheckToken | None, FirebaseError | None], None],
|
|
400
|
+
force_refresh: bool = False,
|
|
401
|
+
) -> None:
|
|
402
|
+
"""Get an App Check token, for example to call your own backend.
|
|
403
|
+
|
|
404
|
+
The token is cached, also across app launches, until it is about to
|
|
405
|
+
expire: the provider is only asked again then, or with ``force_refresh``.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
callback: gets ``(AppCheckToken, None)``, or ``(None, error)``.
|
|
409
|
+
force_refresh: get a new token even if the cached one is valid.
|
|
410
|
+
"""
|
|
411
|
+
|
|
412
|
+
def done(token_ptr: Any, error_ptr: Any) -> None:
|
|
413
|
+
if error_ptr or not token_ptr:
|
|
414
|
+
callback(None, error_from_pointer(error_ptr))
|
|
415
|
+
return
|
|
416
|
+
callback(AppCheckToken.from_native(objc_instance(token_ptr)), None)
|
|
417
|
+
|
|
418
|
+
self._native.tokenForcingRefresh_completion_(
|
|
419
|
+
bool(force_refresh), one_shot_block(done, objc_id, objc_id)
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
def limited_use_token(
|
|
423
|
+
self, callback: Callable[[AppCheckToken | None, FirebaseError | None], None]
|
|
424
|
+
) -> None:
|
|
425
|
+
"""Get a limited-use (single-use) token, for backends with replay
|
|
426
|
+
protection. It is not cached and does not affect ``token``.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
callback: gets ``(AppCheckToken, None)``, or ``(None, error)``.
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
def done(token_ptr: Any, error_ptr: Any) -> None:
|
|
433
|
+
if error_ptr or not token_ptr:
|
|
434
|
+
callback(None, error_from_pointer(error_ptr))
|
|
435
|
+
return
|
|
436
|
+
callback(AppCheckToken.from_native(objc_instance(token_ptr)), None)
|
|
437
|
+
|
|
438
|
+
self._native.limitedUseTokenWithCompletion_(
|
|
439
|
+
one_shot_block(done, objc_id, objc_id)
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
def add_token_listener(
|
|
443
|
+
self, listener: Callable[[str], None]
|
|
444
|
+
) -> AppCheckTokenListener:
|
|
445
|
+
"""Listen for new App Check tokens (each refresh), on the main thread.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
listener: gets the new token string.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
An ``AppCheckTokenListener``. Call ``.remove()`` to stop.
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
def on_notification(notification_ptr: Any) -> None:
|
|
455
|
+
notification = objc_instance(notification_ptr)
|
|
456
|
+
user_info = notification.userInfo
|
|
457
|
+
token = None if user_info is None else user_info.objectForKey_(_TOKEN_KEY)
|
|
458
|
+
if token is not None:
|
|
459
|
+
listener(str(token))
|
|
460
|
+
|
|
461
|
+
block, release = listener_block(on_notification, objc_id)
|
|
462
|
+
observer = objc_class(
|
|
463
|
+
"NSNotificationCenter"
|
|
464
|
+
).defaultCenter.addObserverForName_object_queue_usingBlock_(
|
|
465
|
+
_TOKEN_CHANGED, None, objc_class("NSOperationQueue").mainQueue, block
|
|
466
|
+
)
|
|
467
|
+
return AppCheckTokenListener(observer, release)
|
|
468
|
+
|
|
469
|
+
@staticmethod
|
|
470
|
+
def debug_token() -> str:
|
|
471
|
+
"""The debug provider's token: the ``DEBUG_TOKEN_ENVIRONMENT_VARIABLE``
|
|
472
|
+
token if set, else this device's local one. Register it in the Firebase
|
|
473
|
+
console. Works whichever provider is in use (after ``core.configure()``).
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
The token.
|
|
477
|
+
"""
|
|
478
|
+
provider = (
|
|
479
|
+
objc_class("FIRAppCheckDebugProvider")
|
|
480
|
+
.alloc()
|
|
481
|
+
.initWithApp_(objc_class("FIRApp").defaultApp())
|
|
482
|
+
)
|
|
483
|
+
return str(provider.currentDebugToken())
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Firebase Authentication.
|
|
2
|
+
|
|
3
|
+
from pyios_firebase.auth import Auth
|
|
4
|
+
|
|
5
|
+
auth = Auth.auth()
|
|
6
|
+
auth.create_user_with_email_password("ada@example.com", "s3cret!", on_signed_in)
|
|
7
|
+
listener = auth.add_state_did_change_listener(lambda user: print("user:", user))
|
|
8
|
+
|
|
9
|
+
- ``Auth``: sign-in (email/password, email link, anonymous, custom token,
|
|
10
|
+
credentials, OAuth web flows), sign-out, listeners, password reset and
|
|
11
|
+
action codes, the emulator
|
|
12
|
+
- ``User``: profile, ID tokens and claims, account changes, email
|
|
13
|
+
verification, linking and re-authentication
|
|
14
|
+
- credentials: ``EmailAuthProvider``, ``GoogleAuthProvider``,
|
|
15
|
+
``OAuthProvider`` (Apple, Microsoft, Yahoo, GitHub, OIDC, SAML),
|
|
16
|
+
``PhoneAuthProvider``, ``FacebookAuthProvider``, ``GitHubAuthProvider``,
|
|
17
|
+
``TwitterAuthProvider``, ``GameCenterAuthProvider``
|
|
18
|
+
- ``pyios_firebase.auth.google`` / ``pyios_firebase.auth.apple``: the Google and
|
|
19
|
+
Apple sign-in sheets
|
|
20
|
+
- multi-factor (``pyios_firebase.auth.multi_factor``): SMS and TOTP second factors
|
|
21
|
+
- ``AuthError`` / ``AuthErrorCode``: what failed
|
|
22
|
+
|
|
23
|
+
Needs the ``FirebaseAuth`` framework. Google sign-in also needs
|
|
24
|
+
``GoogleSignIn``; Apple sign-in needs the ``com.apple.developer.applesignin``
|
|
25
|
+
entitlement.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from .auth import (
|
|
29
|
+
APNS_TOKEN_TYPE_PRODUCTION,
|
|
30
|
+
APNS_TOKEN_TYPE_SANDBOX,
|
|
31
|
+
APNS_TOKEN_TYPE_UNKNOWN,
|
|
32
|
+
Auth,
|
|
33
|
+
AuthStateListener,
|
|
34
|
+
)
|
|
35
|
+
from .credentials import (
|
|
36
|
+
AuthCredential,
|
|
37
|
+
EmailAuthProvider,
|
|
38
|
+
FacebookAuthProvider,
|
|
39
|
+
GameCenterAuthProvider,
|
|
40
|
+
GitHubAuthProvider,
|
|
41
|
+
GoogleAuthProvider,
|
|
42
|
+
OAuthCredential,
|
|
43
|
+
OAuthProvider,
|
|
44
|
+
PhoneAuthProvider,
|
|
45
|
+
TwitterAuthProvider,
|
|
46
|
+
)
|
|
47
|
+
from .errors import AUTH_ERROR_DOMAIN, AuthError, AuthErrorCode
|
|
48
|
+
from .multi_factor import (
|
|
49
|
+
PHONE_FACTOR_ID,
|
|
50
|
+
TOTP_FACTOR_ID,
|
|
51
|
+
MultiFactor,
|
|
52
|
+
MultiFactorAssertion,
|
|
53
|
+
MultiFactorInfo,
|
|
54
|
+
MultiFactorResolver,
|
|
55
|
+
MultiFactorSession,
|
|
56
|
+
PhoneMultiFactorGenerator,
|
|
57
|
+
PhoneMultiFactorInfo,
|
|
58
|
+
TOTPMultiFactorGenerator,
|
|
59
|
+
TOTPSecret,
|
|
60
|
+
)
|
|
61
|
+
from .results import (
|
|
62
|
+
ActionCodeInfo,
|
|
63
|
+
ActionCodeOperation,
|
|
64
|
+
ActionCodeSettings,
|
|
65
|
+
ActionCodeURL,
|
|
66
|
+
AdditionalUserInfo,
|
|
67
|
+
AuthDataResult,
|
|
68
|
+
IDTokenResult,
|
|
69
|
+
UserInfo,
|
|
70
|
+
UserMetadata,
|
|
71
|
+
)
|
|
72
|
+
from .user import UNSET, User
|
|
73
|
+
|
|
74
|
+
__all__ = (
|
|
75
|
+
"APNS_TOKEN_TYPE_PRODUCTION",
|
|
76
|
+
"APNS_TOKEN_TYPE_SANDBOX",
|
|
77
|
+
"APNS_TOKEN_TYPE_UNKNOWN",
|
|
78
|
+
"AUTH_ERROR_DOMAIN",
|
|
79
|
+
"PHONE_FACTOR_ID",
|
|
80
|
+
"TOTP_FACTOR_ID",
|
|
81
|
+
"UNSET",
|
|
82
|
+
"ActionCodeInfo",
|
|
83
|
+
"ActionCodeOperation",
|
|
84
|
+
"ActionCodeSettings",
|
|
85
|
+
"ActionCodeURL",
|
|
86
|
+
"AdditionalUserInfo",
|
|
87
|
+
"Auth",
|
|
88
|
+
"AuthCredential",
|
|
89
|
+
"AuthDataResult",
|
|
90
|
+
"AuthError",
|
|
91
|
+
"AuthErrorCode",
|
|
92
|
+
"AuthStateListener",
|
|
93
|
+
"EmailAuthProvider",
|
|
94
|
+
"FacebookAuthProvider",
|
|
95
|
+
"GameCenterAuthProvider",
|
|
96
|
+
"GitHubAuthProvider",
|
|
97
|
+
"GoogleAuthProvider",
|
|
98
|
+
"IDTokenResult",
|
|
99
|
+
"MultiFactor",
|
|
100
|
+
"MultiFactorAssertion",
|
|
101
|
+
"MultiFactorInfo",
|
|
102
|
+
"MultiFactorResolver",
|
|
103
|
+
"MultiFactorSession",
|
|
104
|
+
"OAuthCredential",
|
|
105
|
+
"OAuthProvider",
|
|
106
|
+
"PhoneAuthProvider",
|
|
107
|
+
"PhoneMultiFactorGenerator",
|
|
108
|
+
"PhoneMultiFactorInfo",
|
|
109
|
+
"TOTPMultiFactorGenerator",
|
|
110
|
+
"TOTPSecret",
|
|
111
|
+
"TwitterAuthProvider",
|
|
112
|
+
"User",
|
|
113
|
+
"UserInfo",
|
|
114
|
+
"UserMetadata",
|
|
115
|
+
"current_user",
|
|
116
|
+
"sign_out",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def current_user() -> User | None:
|
|
121
|
+
"""The signed-in ``User``, or ``None``. Same as ``Auth.auth().current_user``."""
|
|
122
|
+
return Auth.auth().current_user
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def sign_out() -> AuthError | None:
|
|
126
|
+
"""Sign the current user out of Firebase. Same as ``Auth.auth().sign_out()``.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
``None``, or the error.
|
|
130
|
+
"""
|
|
131
|
+
return Auth.auth().sign_out()
|