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,592 @@
|
|
|
1
|
+
"""``Auth``: Firebase Authentication for the app. Sign-in, sign-out, listeners,
|
|
2
|
+
password reset and email links."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
__all__ = ("Auth", "AuthStateListener")
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from ctypes import byref
|
|
10
|
+
from typing import Any, ClassVar, Final
|
|
11
|
+
|
|
12
|
+
from rubicon.objc import ns_from_py, objc_id
|
|
13
|
+
|
|
14
|
+
from ..types import ObjCObject
|
|
15
|
+
from ..util import listener_block, objc_class, objc_instance
|
|
16
|
+
from ._common import (
|
|
17
|
+
error_block,
|
|
18
|
+
flag,
|
|
19
|
+
missing_callback_scheme,
|
|
20
|
+
result_block,
|
|
21
|
+
string_block,
|
|
22
|
+
text,
|
|
23
|
+
)
|
|
24
|
+
from .credentials import AuthCredential, OAuthProvider
|
|
25
|
+
from .errors import AuthError, auth_error_from_nserror, simple_auth_error
|
|
26
|
+
from .results import ActionCodeInfo, ActionCodeSettings, AuthDataResult
|
|
27
|
+
from .user import User
|
|
28
|
+
|
|
29
|
+
ErrorCallback = Callable[[AuthError | None], None]
|
|
30
|
+
DataResultCallback = Callable[[AuthDataResult | None, AuthError | None], None]
|
|
31
|
+
UserListener = Callable[[User | None], None]
|
|
32
|
+
|
|
33
|
+
APNS_TOKEN_TYPE_UNKNOWN: Final = 0
|
|
34
|
+
"""``set_apns_token`` type: let Firebase find out (sandbox or production)."""
|
|
35
|
+
APNS_TOKEN_TYPE_SANDBOX: Final = 1
|
|
36
|
+
"""``set_apns_token`` type: a development (sandbox) APNs token."""
|
|
37
|
+
APNS_TOKEN_TYPE_PRODUCTION: Final = 2
|
|
38
|
+
"""``set_apns_token`` type: a production APNs token."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AuthStateListener:
|
|
42
|
+
"""Given back by ``add_state_did_change_listener`` and
|
|
43
|
+
``add_id_token_did_change_listener``. Call ``remove()`` to stop.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
handle: the native listener handle.
|
|
47
|
+
remove: removes the handle from Firebase.
|
|
48
|
+
release: releases the listener's callback block.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
handle: ObjCObject,
|
|
54
|
+
remove: Callable[[ObjCObject], None],
|
|
55
|
+
release: Callable[[], None],
|
|
56
|
+
) -> None:
|
|
57
|
+
self._handle: ObjCObject | None = handle
|
|
58
|
+
self._remove = remove
|
|
59
|
+
self._release = release
|
|
60
|
+
|
|
61
|
+
def remove(self) -> None:
|
|
62
|
+
"""Stop the updates. Safe to call more than once."""
|
|
63
|
+
if self._handle is None:
|
|
64
|
+
return
|
|
65
|
+
self._remove(self._handle)
|
|
66
|
+
self._handle = None
|
|
67
|
+
self._release()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _throwing(call: Callable[[Any], Any]) -> AuthError | None:
|
|
71
|
+
"""Run a Swift ``throws`` method (an ``NSError **`` argument): its error, or ``None``."""
|
|
72
|
+
error = objc_id()
|
|
73
|
+
ok = call(byref(error))
|
|
74
|
+
if ok or not error.value:
|
|
75
|
+
return None
|
|
76
|
+
return auth_error_from_nserror(objc_instance(error))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Auth:
|
|
80
|
+
"""Firebase Authentication. Get it with ``Auth.auth()``.
|
|
81
|
+
|
|
82
|
+
::
|
|
83
|
+
|
|
84
|
+
auth = Auth.auth()
|
|
85
|
+
auth.sign_in_with_email_password("ada@example.com", "s3cret!", on_signed_in)
|
|
86
|
+
|
|
87
|
+
Every callback gets ``(result, error)`` (or just ``(error)``), where
|
|
88
|
+
``error`` is an ``AuthError`` or ``None``.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
native: the native ``FIRAuth``. Leave it out for the default app's.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
_instance: ClassVar[Auth | None] = None
|
|
95
|
+
# native FIRAuth addresses that use the emulator (the SDK does not expose it)
|
|
96
|
+
_emulated: ClassVar[set[int]] = set()
|
|
97
|
+
|
|
98
|
+
def __init__(self, native: ObjCObject | None = None) -> None:
|
|
99
|
+
self._native: ObjCObject = (
|
|
100
|
+
native if native is not None else objc_class("FIRAuth").auth()
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def auth(cls) -> Auth:
|
|
105
|
+
"""The shared ``Auth`` for the default Firebase app."""
|
|
106
|
+
if cls._instance is None:
|
|
107
|
+
cls._instance = cls(objc_class("FIRAuth").auth())
|
|
108
|
+
return cls._instance
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def native(self) -> ObjCObject:
|
|
112
|
+
"""The ``FIRAuth``, for SDK calls this wrapper does not cover."""
|
|
113
|
+
return self._native
|
|
114
|
+
|
|
115
|
+
# ---- the current user ---------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def current_user(self) -> User | None:
|
|
119
|
+
"""The signed-in user, or ``None``.
|
|
120
|
+
|
|
121
|
+
Right after app start this can be ``None`` for a moment while Firebase
|
|
122
|
+
restores the saved user; use ``add_state_did_change_listener`` to know
|
|
123
|
+
for sure.
|
|
124
|
+
"""
|
|
125
|
+
user = self._native.currentUser
|
|
126
|
+
return None if user is None else User(user)
|
|
127
|
+
|
|
128
|
+
def update_current_user(
|
|
129
|
+
self, user: User, callback: ErrorCallback | None = None
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Make ``user`` the signed-in user (for example one from another
|
|
132
|
+
``Auth`` instance or a shared keychain).
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
user: the user to sign in as.
|
|
136
|
+
callback: gets ``None`` or the error.
|
|
137
|
+
"""
|
|
138
|
+
self._native.updateCurrentUser_completion_(user.user, error_block(callback))
|
|
139
|
+
|
|
140
|
+
def sign_out(self) -> AuthError | None:
|
|
141
|
+
"""Sign the current user out of Firebase (Google, Apple and other
|
|
142
|
+
providers keep their own sessions).
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
``None``, or the error (for example a keychain error).
|
|
146
|
+
"""
|
|
147
|
+
return _throwing(self._native.signOut_)
|
|
148
|
+
|
|
149
|
+
# ---- sign-in ----------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
def sign_in_with_email_password(
|
|
152
|
+
self, email: str, password: str, callback: DataResultCallback | None = None
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Sign in with an email address and password.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
email: the email address.
|
|
158
|
+
password: the password.
|
|
159
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
|
|
160
|
+
With email enumeration protection (the default), a wrong email
|
|
161
|
+
or password both give ``INVALID_CREDENTIAL``.
|
|
162
|
+
"""
|
|
163
|
+
self._native.signInWithEmail_password_completion_(
|
|
164
|
+
email, password, result_block(callback, AuthDataResult)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def create_user_with_email_password(
|
|
168
|
+
self, email: str, password: str, callback: DataResultCallback | None = None
|
|
169
|
+
) -> None:
|
|
170
|
+
"""Create an account with an email address and password, and sign in.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
email: the email address.
|
|
174
|
+
password: the password (at least 6 characters, or your project's
|
|
175
|
+
password policy).
|
|
176
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``
|
|
177
|
+
(``EMAIL_ALREADY_IN_USE``, ``WEAK_PASSWORD``, ``INVALID_EMAIL``...).
|
|
178
|
+
"""
|
|
179
|
+
self._native.createUserWithEmail_password_completion_(
|
|
180
|
+
email, password, result_block(callback, AuthDataResult)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def sign_in_anonymously(self, callback: DataResultCallback | None = None) -> None:
|
|
184
|
+
"""Sign in as a new anonymous user (or the current anonymous one).
|
|
185
|
+
|
|
186
|
+
Link a credential later (``User.link_with_credential``) to keep the
|
|
187
|
+
account when the user signs up.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
|
|
191
|
+
"""
|
|
192
|
+
self._native.signInAnonymouslyWithCompletion_(
|
|
193
|
+
result_block(callback, AuthDataResult)
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def sign_in_with_custom_token(
|
|
197
|
+
self, token: str, callback: DataResultCallback | None = None
|
|
198
|
+
) -> None:
|
|
199
|
+
"""Sign in with a custom token made by your backend (Admin SDK
|
|
200
|
+
``create_custom_token``).
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
token: the custom token.
|
|
204
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``
|
|
205
|
+
(``INVALID_CUSTOM_TOKEN``, ``CUSTOM_TOKEN_MISMATCH``).
|
|
206
|
+
"""
|
|
207
|
+
self._native.signInWithCustomToken_completion_(
|
|
208
|
+
token, result_block(callback, AuthDataResult)
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def sign_in_with_credential(
|
|
212
|
+
self, credential: AuthCredential, callback: DataResultCallback | None = None
|
|
213
|
+
) -> None:
|
|
214
|
+
"""Sign in with a credential from any provider.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
credential: the credential, from one of the provider classes.
|
|
218
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
|
|
219
|
+
``ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL`` means the email is
|
|
220
|
+
already used with another provider: sign in with that one, then
|
|
221
|
+
link this credential.
|
|
222
|
+
"""
|
|
223
|
+
self._native.signInWithCredential_completion_(
|
|
224
|
+
credential.native, result_block(callback, AuthDataResult)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def sign_in_with_provider(
|
|
228
|
+
self, provider: OAuthProvider, callback: DataResultCallback | None = None
|
|
229
|
+
) -> None:
|
|
230
|
+
"""Sign in through an OAuth provider's web flow (Microsoft, Yahoo,
|
|
231
|
+
GitHub, OIDC, SAML), in an in-app browser.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
provider: the provider, for example ``OAuthProvider("yahoo.com")``.
|
|
235
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``.
|
|
236
|
+
``error.cancelled`` is True when the user closed the browser.
|
|
237
|
+
If the callback URL scheme is missing from ``Info.plist``, the
|
|
238
|
+
error says which one to add (the SDK itself would stop the app).
|
|
239
|
+
"""
|
|
240
|
+
problem = missing_callback_scheme(self._native)
|
|
241
|
+
if problem is not None:
|
|
242
|
+
if callback is not None:
|
|
243
|
+
callback(None, simple_auth_error(problem))
|
|
244
|
+
return
|
|
245
|
+
self._native.signInWithProvider_UIDelegate_completion_(
|
|
246
|
+
provider.native, None, result_block(callback, AuthDataResult)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# ---- email links ------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
def send_sign_in_link(
|
|
252
|
+
self,
|
|
253
|
+
email: str,
|
|
254
|
+
action_code_settings: ActionCodeSettings,
|
|
255
|
+
callback: ErrorCallback | None = None,
|
|
256
|
+
) -> None:
|
|
257
|
+
"""Email a passwordless sign-in link.
|
|
258
|
+
|
|
259
|
+
Save ``email`` on the device: you need it again in
|
|
260
|
+
``sign_in_with_email_link``.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
email: where to send the link.
|
|
264
|
+
action_code_settings: where the link leads; ``handle_code_in_app``
|
|
265
|
+
must be True.
|
|
266
|
+
callback: gets ``None`` once the email is sent, or the error.
|
|
267
|
+
"""
|
|
268
|
+
self._native.sendSignInLinkToEmail_actionCodeSettings_completion_(
|
|
269
|
+
email, action_code_settings.to_native(), error_block(callback)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def is_sign_in_with_email_link(self, link: str) -> bool:
|
|
273
|
+
"""Whether ``link`` (a URL the app was opened with) is a sign-in link.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
link: the full URL.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
True if it can be passed to ``sign_in_with_email_link``.
|
|
280
|
+
"""
|
|
281
|
+
return bool(self._native.isSignInWithEmailLink_(link))
|
|
282
|
+
|
|
283
|
+
def sign_in_with_email_link(
|
|
284
|
+
self, email: str, link: str, callback: DataResultCallback | None = None
|
|
285
|
+
) -> None:
|
|
286
|
+
"""Sign in with an email link from ``send_sign_in_link``.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
email: the address the link was sent to.
|
|
290
|
+
link: the full link the user opened.
|
|
291
|
+
callback: gets ``(AuthDataResult, None)``, or ``(None, error)``
|
|
292
|
+
(``INVALID_ACTION_CODE`` / ``EXPIRED_ACTION_CODE``).
|
|
293
|
+
"""
|
|
294
|
+
self._native.signInWithEmail_link_completion_(
|
|
295
|
+
email, link, result_block(callback, AuthDataResult)
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# ---- password reset and action codes --------------------------------------
|
|
299
|
+
|
|
300
|
+
def send_password_reset(
|
|
301
|
+
self,
|
|
302
|
+
email: str,
|
|
303
|
+
callback: ErrorCallback | None = None,
|
|
304
|
+
action_code_settings: ActionCodeSettings | None = None,
|
|
305
|
+
) -> None:
|
|
306
|
+
"""Email a password reset link.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
email: the account's email address.
|
|
310
|
+
callback: gets ``None`` once the email is sent, or the error. With
|
|
311
|
+
email enumeration protection on, an unknown address is not an
|
|
312
|
+
error.
|
|
313
|
+
action_code_settings: where the link leads, or ``None`` for the
|
|
314
|
+
Firebase default page.
|
|
315
|
+
"""
|
|
316
|
+
settings = (
|
|
317
|
+
None if action_code_settings is None else action_code_settings.to_native()
|
|
318
|
+
)
|
|
319
|
+
self._native.sendPasswordResetWithEmail_actionCodeSettings_completion_(
|
|
320
|
+
email, settings, error_block(callback)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
def verify_password_reset_code(
|
|
324
|
+
self, code: str, callback: Callable[[str | None, AuthError | None], None]
|
|
325
|
+
) -> None:
|
|
326
|
+
"""Check a password reset code (the ``oobCode`` of the link).
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
code: the code.
|
|
330
|
+
callback: gets ``(email, None)`` with the account's address, or
|
|
331
|
+
``(None, error)``.
|
|
332
|
+
"""
|
|
333
|
+
self._native.verifyPasswordResetCode_completion_(code, string_block(callback))
|
|
334
|
+
|
|
335
|
+
def confirm_password_reset(
|
|
336
|
+
self, code: str, new_password: str, callback: ErrorCallback | None = None
|
|
337
|
+
) -> None:
|
|
338
|
+
"""Set a new password with a password reset code.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
code: the ``oobCode`` of the reset link.
|
|
342
|
+
new_password: the new password.
|
|
343
|
+
callback: gets ``None`` or the error.
|
|
344
|
+
"""
|
|
345
|
+
self._native.confirmPasswordResetWithCode_newPassword_completion_(
|
|
346
|
+
code, new_password, error_block(callback)
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def check_action_code(
|
|
350
|
+
self,
|
|
351
|
+
code: str,
|
|
352
|
+
callback: Callable[[ActionCodeInfo | None, AuthError | None], None],
|
|
353
|
+
) -> None:
|
|
354
|
+
"""Find out what an email action code is for, without using it.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
code: the ``oobCode`` of the link.
|
|
358
|
+
callback: gets ``(ActionCodeInfo, None)``, or ``(None, error)``.
|
|
359
|
+
"""
|
|
360
|
+
self._native.checkActionCode_completion_(
|
|
361
|
+
code, result_block(callback, ActionCodeInfo.from_native)
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def apply_action_code(
|
|
365
|
+
self, code: str, callback: ErrorCallback | None = None
|
|
366
|
+
) -> None:
|
|
367
|
+
"""Use an email action code: verify an email address, or recover / change one.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
code: the ``oobCode`` of the link.
|
|
371
|
+
callback: gets ``None`` or the error.
|
|
372
|
+
"""
|
|
373
|
+
self._native.applyActionCode_completion_(code, error_block(callback))
|
|
374
|
+
|
|
375
|
+
def fetch_sign_in_methods(
|
|
376
|
+
self, email: str, callback: Callable[[list[str] | None, AuthError | None], None]
|
|
377
|
+
) -> None:
|
|
378
|
+
"""The sign-in methods used with ``email``.
|
|
379
|
+
|
|
380
|
+
Deprecated by Firebase: with email enumeration protection on (the
|
|
381
|
+
default), the list is always empty.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
email: the email address.
|
|
385
|
+
callback: gets ``(methods, None)``, for example ``["password"]``, or
|
|
386
|
+
``(None, error)``.
|
|
387
|
+
"""
|
|
388
|
+
self._native.fetchSignInMethodsForEmail_completion_(
|
|
389
|
+
email, result_block(callback, lambda methods: [str(m) for m in methods])
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
def revoke_token(
|
|
393
|
+
self, authorization_code: str, callback: ErrorCallback | None = None
|
|
394
|
+
) -> None:
|
|
395
|
+
"""Revoke a Sign in with Apple token, before deleting an Apple user
|
|
396
|
+
(App Store rule).
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
authorization_code: the ``authorizationCode`` Apple gave on a recent
|
|
400
|
+
sign-in.
|
|
401
|
+
callback: gets ``None`` or the error.
|
|
402
|
+
"""
|
|
403
|
+
self._native.revokeTokenWithAuthorizationCode_completion_(
|
|
404
|
+
authorization_code, error_block(callback)
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# ---- listeners ----------------------------------------------------------------
|
|
408
|
+
|
|
409
|
+
def _listen(
|
|
410
|
+
self, add: str, remove: str, listener: UserListener
|
|
411
|
+
) -> AuthStateListener:
|
|
412
|
+
def on_change(_auth_ptr: Any, user_ptr: Any) -> None:
|
|
413
|
+
listener(User(objc_instance(user_ptr)) if user_ptr else None)
|
|
414
|
+
|
|
415
|
+
block, release = listener_block(on_change, objc_id, objc_id)
|
|
416
|
+
handle = getattr(self._native, add)(block)
|
|
417
|
+
return AuthStateListener(handle, getattr(self._native, remove), release)
|
|
418
|
+
|
|
419
|
+
def add_state_did_change_listener(
|
|
420
|
+
self, listener: UserListener
|
|
421
|
+
) -> AuthStateListener:
|
|
422
|
+
"""Listen to sign-in and sign-out.
|
|
423
|
+
|
|
424
|
+
``listener`` runs now with the current user (or ``None``), then each
|
|
425
|
+
time a user signs in or out.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
listener: gets the ``User``, or ``None`` when signed out.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
An ``AuthStateListener``. Call ``.remove()`` to stop.
|
|
432
|
+
"""
|
|
433
|
+
return self._listen(
|
|
434
|
+
"addAuthStateDidChangeListener_",
|
|
435
|
+
"removeAuthStateDidChangeListener_",
|
|
436
|
+
listener,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
def add_id_token_did_change_listener(
|
|
440
|
+
self, listener: UserListener
|
|
441
|
+
) -> AuthStateListener:
|
|
442
|
+
"""Listen to sign-in, sign-out and ID token refreshes (about once an
|
|
443
|
+
hour), for example to keep your backend's copy of the token fresh.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
listener: gets the ``User``, or ``None`` when signed out.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
An ``AuthStateListener``. Call ``.remove()`` to stop.
|
|
450
|
+
"""
|
|
451
|
+
return self._listen(
|
|
452
|
+
"addIDTokenDidChangeListener_", "removeIDTokenDidChangeListener_", listener
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# ---- settings -----------------------------------------------------------------
|
|
456
|
+
|
|
457
|
+
def use_emulator(self, host: str = "127.0.0.1", port: int = 9099) -> None:
|
|
458
|
+
"""Send this app's Auth calls to the local Auth emulator.
|
|
459
|
+
|
|
460
|
+
Call it right after ``core.configure()``, before any other Auth call.
|
|
461
|
+
Use it only in development and test builds. It also turns app
|
|
462
|
+
verification off, so phone sign-in works without APNs or reCAPTCHA.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
host: the emulator's host. ``"127.0.0.1"`` works in the iOS
|
|
466
|
+
Simulator; on a device, use the Mac's LAN address.
|
|
467
|
+
port: the emulator's port (``firebase emulators:start`` uses 9099).
|
|
468
|
+
"""
|
|
469
|
+
self._native.useEmulatorWithHost_port_(host, int(port))
|
|
470
|
+
Auth._emulated.add(int(self._native.ptr.value or 0))
|
|
471
|
+
|
|
472
|
+
@property
|
|
473
|
+
def is_using_emulator(self) -> bool:
|
|
474
|
+
"""True after ``use_emulator``."""
|
|
475
|
+
return int(self._native.ptr.value or 0) in Auth._emulated
|
|
476
|
+
|
|
477
|
+
@property
|
|
478
|
+
def language_code(self) -> str | None:
|
|
479
|
+
"""The language of emails, SMS and web flows Firebase sends, for
|
|
480
|
+
example ``"fr"``; ``None`` for the project default."""
|
|
481
|
+
return text(self._native.languageCode)
|
|
482
|
+
|
|
483
|
+
@language_code.setter
|
|
484
|
+
def language_code(self, code: str | None) -> None:
|
|
485
|
+
self._native.languageCode = code
|
|
486
|
+
|
|
487
|
+
def use_app_language(self) -> None:
|
|
488
|
+
"""Set ``language_code`` to the app's current language."""
|
|
489
|
+
self._native.useAppLanguage()
|
|
490
|
+
|
|
491
|
+
@property
|
|
492
|
+
def tenant_id(self) -> str | None:
|
|
493
|
+
"""The tenant users sign in to (multi-tenancy with Identity Platform),
|
|
494
|
+
or ``None``."""
|
|
495
|
+
return text(self._native.tenantID)
|
|
496
|
+
|
|
497
|
+
@tenant_id.setter
|
|
498
|
+
def tenant_id(self, tenant_id: str | None) -> None:
|
|
499
|
+
self._native.tenantID = tenant_id
|
|
500
|
+
|
|
501
|
+
@property
|
|
502
|
+
def app_verification_disabled_for_testing(self) -> bool:
|
|
503
|
+
"""Skip app verification for phone sign-in with test numbers (set in
|
|
504
|
+
the Firebase console). For tests only."""
|
|
505
|
+
settings = self._native.settings
|
|
506
|
+
return settings is not None and flag(
|
|
507
|
+
settings, "isAppVerificationDisabledForTesting"
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
@app_verification_disabled_for_testing.setter
|
|
511
|
+
def app_verification_disabled_for_testing(self, disabled: bool) -> None:
|
|
512
|
+
settings = self._native.settings.copy()
|
|
513
|
+
settings.appVerificationDisabledForTesting = bool(disabled)
|
|
514
|
+
self._native.settings = settings
|
|
515
|
+
|
|
516
|
+
def use_user_access_group(self, access_group: str | None) -> AuthError | None:
|
|
517
|
+
"""Share the signed-in user with other apps (and extensions) through a
|
|
518
|
+
keychain access group.
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
access_group: the keychain group (with your team ID prefix), or
|
|
522
|
+
``None`` to stop sharing.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
``None``, or the error (for example a keychain error).
|
|
526
|
+
"""
|
|
527
|
+
return _throwing(
|
|
528
|
+
lambda error: self._native.useUserAccessGroup_error_(access_group, error)
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
def get_stored_user(
|
|
532
|
+
self, access_group: str | None
|
|
533
|
+
) -> tuple[User | None, AuthError | None]:
|
|
534
|
+
"""The user saved in a keychain access group, without switching to it
|
|
535
|
+
(for example the user signed in to your other app).
|
|
536
|
+
|
|
537
|
+
Sign in as them with ``update_current_user``.
|
|
538
|
+
|
|
539
|
+
Args:
|
|
540
|
+
access_group: the keychain access group (with your team ID
|
|
541
|
+
prefix), or ``None`` for the app's own keychain.
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
``(user, None)``, ``(None, None)`` if no user is saved there, or
|
|
545
|
+
``(None, error)`` (for example without the Keychain Sharing
|
|
546
|
+
capability).
|
|
547
|
+
"""
|
|
548
|
+
error = objc_id()
|
|
549
|
+
user = self._native.getStoredUserForAccessGroup_error_(
|
|
550
|
+
access_group, byref(error)
|
|
551
|
+
)
|
|
552
|
+
if error.value:
|
|
553
|
+
return None, auth_error_from_nserror(objc_instance(error))
|
|
554
|
+
return (None if user is None else User(user)), None
|
|
555
|
+
|
|
556
|
+
# ---- app delegate hooks (phone and OAuth sign-in) ---------------------------
|
|
557
|
+
|
|
558
|
+
def can_handle_url(self, url: str) -> bool:
|
|
559
|
+
"""Give a URL the app was opened with to Firebase Auth (reCAPTCHA and
|
|
560
|
+
OAuth redirects).
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
url: the URL.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
True if Firebase Auth handled it.
|
|
567
|
+
"""
|
|
568
|
+
return bool(self._native.canHandleURL_(objc_class("NSURL").URLWithString_(url)))
|
|
569
|
+
|
|
570
|
+
def can_handle_notification(self, user_info: dict[str, Any]) -> bool:
|
|
571
|
+
"""Give a remote notification to Firebase Auth (silent push for phone
|
|
572
|
+
sign-in).
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
user_info: the notification's ``userInfo``.
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
True if it was for Firebase Auth.
|
|
579
|
+
"""
|
|
580
|
+
return bool(self._native.canHandleNotification_(ns_from_py(user_info)))
|
|
581
|
+
|
|
582
|
+
def set_apns_token(
|
|
583
|
+
self, token: bytes, token_type: int = APNS_TOKEN_TYPE_UNKNOWN
|
|
584
|
+
) -> None:
|
|
585
|
+
"""Give Firebase Auth the app's APNs device token (phone sign-in).
|
|
586
|
+
|
|
587
|
+
Args:
|
|
588
|
+
token: the device token from
|
|
589
|
+
``didRegisterForRemoteNotificationsWithDeviceToken``.
|
|
590
|
+
token_type: ``APNS_TOKEN_TYPE_UNKNOWN`` / ``SANDBOX`` / ``PRODUCTION``.
|
|
591
|
+
"""
|
|
592
|
+
self._native.setAPNSToken_type_(ns_from_py(bytes(token)), int(token_type))
|